From f8a2b2376559512ba3bd185eff04e20a37c5c488 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Fri, 17 Oct 2025 10:02:48 +0200 Subject: [PATCH 01/79] fix(cicd): update renovate.json to preserve semver ranges --- renovate.json | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/renovate.json b/renovate.json index a024b67..0b3d5ad 100644 --- a/renovate.json +++ b/renovate.json @@ -4,34 +4,22 @@ "config:best-practices", ":approveMajorUpdates", ":maintainLockFilesWeekly", + ":preserveSemverRanges", "schedule:weekly" ], "packageRules": [ { "groupName": "backend", - "matchFileNames": [ - "backend/pyproject.toml", - "backend/.python-version", - "backend/Dockerfile*" - ] + "matchFileNames": ["backend/pyproject.toml", "backend/.python-version", "backend/Dockerfile*"] }, { "groupName": "frontend", - "matchFileNames": [ - "frontend/package.json", - "frontend/Dockerfile*" - ] + "matchFileNames": ["frontend/package.json", "frontend/Dockerfile*"] }, { "groupName": "infrastructure", - "matchFileNames": [ - "**/compose.*.yml", - "**/compose.yml" - ] + "matchFileNames": ["**/compose.*.yml", "**/compose.yml"] } ], - "labels": [ - "dependencies", - "renovate" - ] + "labels": ["dependencies", "renovate"] } From 30d992b12f13bfcbdbff8ec05c10ba9ad6f02fdc Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Fri, 17 Oct 2025 14:11:14 +0200 Subject: [PATCH 02/79] fix(backend): Include disposable email check in backend registration endpoint --- .pre-commit-config.yaml | 2 +- backend/app/api/auth/crud/users.py | 6 +++++- backend/app/api/auth/exceptions.py | 10 ++++++++++ backend/app/api/auth/utils/email_validation.py | 13 +++++++------ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ed4ace6..767c39b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: gitleaks - repo: https://github.com/executablebooks/mdformat - rev: 1.0.0 + rev: 0.7.22 hooks: - id: mdformat # Format Markdown files. additional_dependencies: diff --git a/backend/app/api/auth/crud/users.py b/backend/app/api/auth/crud/users.py index 5961c18..d2906e5 100644 --- a/backend/app/api/auth/crud/users.py +++ b/backend/app/api/auth/crud/users.py @@ -6,7 +6,7 @@ from sqlmodel import select from sqlmodel.ext.asyncio.session import AsyncSession -from app.api.auth.exceptions import UserNameAlreadyExistsError +from app.api.auth.exceptions import DisposableEmailError, UserNameAlreadyExistsError from app.api.auth.models import Organization, OrganizationRole, User from app.api.auth.schemas import ( OrganizationCreate, @@ -14,6 +14,7 @@ UserCreateWithOrganization, UserUpdate, ) +from app.api.auth.utils.email_validation import is_disposable_email from app.api.common.crud.utils import db_get_model_with_id_if_it_exists @@ -27,6 +28,9 @@ async def create_user_override( """ # TODO: Fix type errors in this method and implement custom UserNameAlreadyExists error in FastAPI-Users + if await is_disposable_email(user_create.email): + raise DisposableEmailError(email=user_create.email) + if user_create.username is not None: query = select(User).where(User.username == user_create.username) existing_username = await user_db.session.execute(query) diff --git a/backend/app/api/auth/exceptions.py b/backend/app/api/auth/exceptions.py index d03bdef..3ba0302 100644 --- a/backend/app/api/auth/exceptions.py +++ b/backend/app/api/auth/exceptions.py @@ -126,3 +126,13 @@ def __init__( ) -> None: model_name = model_type.get_api_model_name().name_capital super().__init__(message=(f"User {user_id} does not own {model_name} with ID {model_id}.")) + + +class DisposableEmailError(AuthCRUDError): + """Raised when a disposable email address is used.""" + + http_status_code = status.HTTP_400_BAD_REQUEST + + def __init__(self, email: str) -> None: + msg = f"The email address '{email}' is from a disposable email provider, which is not allowed." + super().__init__(msg) diff --git a/backend/app/api/auth/utils/email_validation.py b/backend/app/api/auth/utils/email_validation.py index b4e3ac1..36dfefa 100644 --- a/backend/app/api/auth/utils/email_validation.py +++ b/backend/app/api/auth/utils/email_validation.py @@ -1,10 +1,10 @@ -# backend/app/api/auth/utils/email_validation.py +"""Utilities for validating email addresses.""" + from datetime import UTC, datetime, timedelta from pathlib import Path import anyio import httpx -from fastapi import HTTPException DISPOSABLE_DOMAINS_URL = "https://raw.githubusercontent.com/disposable/disposable-email-domains/master/domains.txt" BASE_DIR: Path = (Path(__file__).parents[4]).resolve() @@ -20,7 +20,7 @@ async def get_disposable_domains() -> set[str]: cache_age = datetime.now(tz=UTC) - datetime.fromtimestamp(CACHE_FILE.stat().st_mtime, tz=UTC) if cache_age < CACHE_DURATION: async with await anyio.open_file(CACHE_FILE, "r") as f: - content = await f.read() # Read the entire file first + content = await f.read() return {line.strip().lower() for line in content.splitlines() if line.strip()} # Fetch fresh list @@ -38,13 +38,14 @@ async def get_disposable_domains() -> set[str]: await f.write("\n".join(sorted(domains))) return domains - except Exception as e: + except (httpx.RequestError, httpx.HTTPStatusError, OSError): # If fetch fails and cache exists, use stale cache if CACHE_FILE.exists(): async with await anyio.open_file(CACHE_FILE, "r") as f: - content = await f.read() # Read the entire file first + content = await f.read() return {line.strip().lower() for line in content.splitlines() if line.strip()} - raise HTTPException(status_code=503, detail="Email validation service unavailable") from e + # If no cache available, return empty set (allow registration) + return set() async def is_disposable_email(email: str) -> bool: From 6b7ca36313d897f6347ab208189f403ed409bd81 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 21 Oct 2025 12:49:38 +0200 Subject: [PATCH 03/79] fix(cicd): Fix versioning issues across the repo --- .pre-commit-config.yaml | 7 +- backend/.python-version | 2 +- backend/Dockerfile | 2 +- backend/Dockerfile.migrations | 2 +- backend/pyproject.toml | 2 +- backend/uv.lock | 751 +- frontend-app/src/assets/data/demo.json | 50791 ------------------- frontend-app/src/assets/data/products.json | 109 - 8 files changed, 384 insertions(+), 51282 deletions(-) delete mode 100644 frontend-app/src/assets/data/demo.json delete mode 100644 frontend-app/src/assets/data/products.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 767c39b..69be1fe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,6 +9,8 @@ repos: rev: v0.8.0 hooks: - id: pre-commit-update # Autoupdate pre-commit hooks + # TODO: Re-add mdformat to pre-commit-update when mdformat plugins are compatible with mdformat 1.0.0 + args: [--exclude, mdformat] - repo: https://github.com/gitleaks/gitleaks rev: v8.28.0 @@ -24,7 +26,6 @@ repos: - mdformat-footnote - mdformat-frontmatter - mdformat-ruff # Support Python code blocks linted with Ruff. - - mdformat-tables # Support GitHub style tables. - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 @@ -63,7 +64,7 @@ repos: entry: pyright --project backend - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.0 + rev: v0.14.1 hooks: - id: ruff-check # Lint code files: ^backend/(app|scripts|tests)/ @@ -73,7 +74,7 @@ repos: args: ["--config", "backend/pyproject.toml"] - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.3 + rev: 0.9.4 hooks: - id: uv-lock # Update the uv lockfile for the backend. files: ^backend/(uv\.lock|pyproject\.toml|uv\.toml)$ diff --git a/backend/.python-version b/backend/.python-version index 6324d40..24ee5b1 100644 --- a/backend/.python-version +++ b/backend/.python-version @@ -1 +1 @@ -3.14 +3.13 diff --git a/backend/Dockerfile b/backend/Dockerfile index 74b22a8..0d3fee1 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -34,7 +34,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --locked --no-editable --no-default-groups --group=api # --- Final runtime stage --- -FROM python:3.14-slim@sha256:1e7c3510ceb3d6ebb499c86e1c418b95cb4e5e2f682f8e195069f470135f8d51 +FROM python:3.13-slim # Build arguments ARG WORKDIR=/opt/relab/backend diff --git a/backend/Dockerfile.migrations b/backend/Dockerfile.migrations index 5f8b17a..92cecd6 100644 --- a/backend/Dockerfile.migrations +++ b/backend/Dockerfile.migrations @@ -33,7 +33,7 @@ COPY scripts/ scripts/ COPY app/ app/ # --- Final runtime stage --- -FROM python:3.14-slim@sha256:1e7c3510ceb3d6ebb499c86e1c418b95cb4e5e2f682f8e195069f470135f8d51 +FROM python:3.13-slim # Build arguments ARG WORKDIR=/opt/relab/backend_migrations diff --git a/backend/pyproject.toml b/backend/pyproject.toml index c5198a9..4ed11cf 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -40,7 +40,7 @@ "markdown>=3.8.2", "pillow >=11.2.1", "psycopg[binary] >=3.2.9", - # TODO: Investigate pydantic v2.12 compatibility issues (might have to do with custom fastapi-users-db-sqlmodel fork) + # TODO: Upgrade to python 3.14 and pydantic 2.12 when SQLModel fixes compatibility issues (see https://github.com/fastapi/sqlmodel/issues/1606) "pydantic >=2.11,<2.12", "pydantic-extra-types >=2.10.5", "pydantic-settings >=2.10.1", diff --git a/backend/uv.lock b/backend/uv.lock index 5d16955..99661a9 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -8,11 +8,11 @@ resolution-markers = [ [[package]] name = "aiosmtplib" -version = "4.0.2" +version = "5.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/e1/cc58e0be242f0b410707e001ed22c689435964fcaab42108887426e44fff/aiosmtplib-4.0.2.tar.gz", hash = "sha256:f0b4933e7270a8be2b588761e5b12b7334c11890ee91987c2fb057e72f566da6", size = 61052, upload-time = "2025-08-25T02:39:07.249Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/15/c2dc93a58d716bce64b53918d3cf667d86c96a56a9f3a239a9f104643637/aiosmtplib-5.0.0.tar.gz", hash = "sha256:514ac11c31cb767c764077eb3c2eb2ae48df6f63f1e847aeb36119c4fc42b52d", size = 61057, upload-time = "2025-10-19T19:12:31.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/2f/db9414bbeacee48ab0c7421a0319b361b7c15b5c3feebcd38684f5d5f849/aiosmtplib-4.0.2-py3-none-any.whl", hash = "sha256:72491f96e6de035c28d29870186782eccb2f651db9c5f8a32c9db689327f5742", size = 27048, upload-time = "2025-08-25T02:39:06.089Z" }, + { url = "https://files.pythonhosted.org/packages/99/42/b997c306dc54e6ac62a251787f6b5ec730797eea08e0336d8f0d7b899d5f/aiosmtplib-5.0.0-py3-none-any.whl", hash = "sha256:95eb0f81189780845363ab0627e7f130bca2d0060d46cd3eeb459f066eb7df32", size = 27048, upload-time = "2025-10-19T19:12:30.124Z" }, ] [[package]] @@ -200,30 +200,30 @@ wheels = [ [[package]] name = "boto3" -version = "1.40.49" +version = "1.40.55" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/32/5b/165dbfc6de77774b0dac5582ac8a7aa92652d61215871ff4c88854864fb0/boto3-1.40.49.tar.gz", hash = "sha256:ea37d133548fbae543092ada61aeb08bced8f9aecd2e96e803dc8237459a80a0", size = 111572, upload-time = "2025-10-09T19:21:49.295Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/d8/a279c054e0c9731172f05b3d118f3ffc9d74806657f84fc0c93c42d1bb5d/boto3-1.40.55.tar.gz", hash = "sha256:27e35b4fa9edd414ce06c1a748bf57cacd8203271847d93fc1053e4a4ec6e1a9", size = 111590, upload-time = "2025-10-17T19:34:56.753Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/07/9b622ec8691911e3420c9872a50a9d333d4880d217e9eb25b327193099dc/boto3-1.40.49-py3-none-any.whl", hash = "sha256:64eb7af5f66998b34ad629786ff4a7f81d74c2d4ef9e42f69d99499dbee46d07", size = 139345, upload-time = "2025-10-09T19:21:46.886Z" }, + { url = "https://files.pythonhosted.org/packages/42/8c/559c6145d857ed953536a83f3a94915bbd5d3d2d406db1abf8bf40be7645/boto3-1.40.55-py3-none-any.whl", hash = "sha256:2e30f5a0d49e107b8a5c0c487891afd300bfa410e1d918bf187ae45ac3839332", size = 139322, upload-time = "2025-10-17T19:34:55.028Z" }, ] [[package]] name = "botocore" -version = "1.40.49" +version = "1.40.55" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/01/6a/eb7503536552bbd3388b2607bc7a64e59d4f988336406b51a69d29f17ed2/botocore-1.40.49.tar.gz", hash = "sha256:fe8d4cbcc22de84c20190ae728c46b931bafeb40fce247010fb071c31b6532b5", size = 14415240, upload-time = "2025-10-09T19:21:37.133Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a4/92/dce4842b2e215d213d34b064fcdd13c6a782c43344e77336bcde586e9229/botocore-1.40.55.tar.gz", hash = "sha256:79b6472e2de92b3519d44fc1eec8c5feced7f99a0d10fdea6dc93133426057c1", size = 14446917, upload-time = "2025-10-17T19:34:47.44Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/7b/dce396a3f7078e0432d40a9778602cbf0785ca91e7bcb64e05f19dfb5662/botocore-1.40.49-py3-none-any.whl", hash = "sha256:bf1089d0e77e4fc2e195d81c519b194ab62a4d4dd3e7113ee4e2bf903b0b75ab", size = 14085172, upload-time = "2025-10-09T19:21:32.721Z" }, + { url = "https://files.pythonhosted.org/packages/21/30/f13bbc36e83b78777ff1abf50a084efcc3336b808e76560d8c5a0c9219e0/botocore-1.40.55-py3-none-any.whl", hash = "sha256:cdc38f7a4ddb30a2cd1cdd4fabde2a5a16e41b5a642292e1c30de5c4e46f5d44", size = 14116107, upload-time = "2025-10-17T19:34:44.398Z" }, ] [[package]] @@ -365,119 +365,119 @@ wheels = [ [[package]] name = "coverage" -version = "7.10.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/26/d22c300112504f5f9a9fd2297ce33c35f3d353e4aeb987c8419453b2a7c2/coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239", size = 827704, upload-time = "2025-09-21T20:03:56.815Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/94/b765c1abcb613d103b64fcf10395f54d69b0ef8be6a0dd9c524384892cc7/coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d", size = 218320, upload-time = "2025-09-21T20:01:56.629Z" }, - { url = "https://files.pythonhosted.org/packages/72/4f/732fff31c119bb73b35236dd333030f32c4bfe909f445b423e6c7594f9a2/coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b", size = 218575, upload-time = "2025-09-21T20:01:58.203Z" }, - { url = "https://files.pythonhosted.org/packages/87/02/ae7e0af4b674be47566707777db1aa375474f02a1d64b9323e5813a6cdd5/coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e", size = 249568, upload-time = "2025-09-21T20:01:59.748Z" }, - { url = "https://files.pythonhosted.org/packages/a2/77/8c6d22bf61921a59bce5471c2f1f7ac30cd4ac50aadde72b8c48d5727902/coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b", size = 252174, upload-time = "2025-09-21T20:02:01.192Z" }, - { url = "https://files.pythonhosted.org/packages/b1/20/b6ea4f69bbb52dac0aebd62157ba6a9dddbfe664f5af8122dac296c3ee15/coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49", size = 253447, upload-time = "2025-09-21T20:02:02.701Z" }, - { url = "https://files.pythonhosted.org/packages/f9/28/4831523ba483a7f90f7b259d2018fef02cb4d5b90bc7c1505d6e5a84883c/coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911", size = 249779, upload-time = "2025-09-21T20:02:04.185Z" }, - { url = "https://files.pythonhosted.org/packages/a7/9f/4331142bc98c10ca6436d2d620c3e165f31e6c58d43479985afce6f3191c/coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0", size = 251604, upload-time = "2025-09-21T20:02:06.034Z" }, - { url = "https://files.pythonhosted.org/packages/ce/60/bda83b96602036b77ecf34e6393a3836365481b69f7ed7079ab85048202b/coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f", size = 249497, upload-time = "2025-09-21T20:02:07.619Z" }, - { url = "https://files.pythonhosted.org/packages/5f/af/152633ff35b2af63977edd835d8e6430f0caef27d171edf2fc76c270ef31/coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c", size = 249350, upload-time = "2025-09-21T20:02:10.34Z" }, - { url = "https://files.pythonhosted.org/packages/9d/71/d92105d122bd21cebba877228990e1646d862e34a98bb3374d3fece5a794/coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f", size = 251111, upload-time = "2025-09-21T20:02:12.122Z" }, - { url = "https://files.pythonhosted.org/packages/a2/9e/9fdb08f4bf476c912f0c3ca292e019aab6712c93c9344a1653986c3fd305/coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698", size = 220746, upload-time = "2025-09-21T20:02:13.919Z" }, - { url = "https://files.pythonhosted.org/packages/b1/b1/a75fd25df44eab52d1931e89980d1ada46824c7a3210be0d3c88a44aaa99/coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843", size = 221541, upload-time = "2025-09-21T20:02:15.57Z" }, - { url = "https://files.pythonhosted.org/packages/14/3a/d720d7c989562a6e9a14b2c9f5f2876bdb38e9367126d118495b89c99c37/coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546", size = 220170, upload-time = "2025-09-21T20:02:17.395Z" }, - { url = "https://files.pythonhosted.org/packages/bb/22/e04514bf2a735d8b0add31d2b4ab636fc02370730787c576bb995390d2d5/coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c", size = 219029, upload-time = "2025-09-21T20:02:18.936Z" }, - { url = "https://files.pythonhosted.org/packages/11/0b/91128e099035ece15da3445d9015e4b4153a6059403452d324cbb0a575fa/coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15", size = 219259, upload-time = "2025-09-21T20:02:20.44Z" }, - { url = "https://files.pythonhosted.org/packages/8b/51/66420081e72801536a091a0c8f8c1f88a5c4bf7b9b1bdc6222c7afe6dc9b/coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4", size = 260592, upload-time = "2025-09-21T20:02:22.313Z" }, - { url = "https://files.pythonhosted.org/packages/5d/22/9b8d458c2881b22df3db5bb3e7369e63d527d986decb6c11a591ba2364f7/coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0", size = 262768, upload-time = "2025-09-21T20:02:24.287Z" }, - { url = "https://files.pythonhosted.org/packages/f7/08/16bee2c433e60913c610ea200b276e8eeef084b0d200bdcff69920bd5828/coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0", size = 264995, upload-time = "2025-09-21T20:02:26.133Z" }, - { url = "https://files.pythonhosted.org/packages/20/9d/e53eb9771d154859b084b90201e5221bca7674ba449a17c101a5031d4054/coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65", size = 259546, upload-time = "2025-09-21T20:02:27.716Z" }, - { url = "https://files.pythonhosted.org/packages/ad/b0/69bc7050f8d4e56a89fb550a1577d5d0d1db2278106f6f626464067b3817/coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541", size = 262544, upload-time = "2025-09-21T20:02:29.216Z" }, - { url = "https://files.pythonhosted.org/packages/ef/4b/2514b060dbd1bc0aaf23b852c14bb5818f244c664cb16517feff6bb3a5ab/coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6", size = 260308, upload-time = "2025-09-21T20:02:31.226Z" }, - { url = "https://files.pythonhosted.org/packages/54/78/7ba2175007c246d75e496f64c06e94122bdb914790a1285d627a918bd271/coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999", size = 258920, upload-time = "2025-09-21T20:02:32.823Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b3/fac9f7abbc841409b9a410309d73bfa6cfb2e51c3fada738cb607ce174f8/coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2", size = 261434, upload-time = "2025-09-21T20:02:34.86Z" }, - { url = "https://files.pythonhosted.org/packages/ee/51/a03bec00d37faaa891b3ff7387192cef20f01604e5283a5fabc95346befa/coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a", size = 221403, upload-time = "2025-09-21T20:02:37.034Z" }, - { url = "https://files.pythonhosted.org/packages/53/22/3cf25d614e64bf6d8e59c7c669b20d6d940bb337bdee5900b9ca41c820bb/coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb", size = 222469, upload-time = "2025-09-21T20:02:39.011Z" }, - { url = "https://files.pythonhosted.org/packages/49/a1/00164f6d30d8a01c3c9c48418a7a5be394de5349b421b9ee019f380df2a0/coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb", size = 220731, upload-time = "2025-09-21T20:02:40.939Z" }, - { url = "https://files.pythonhosted.org/packages/23/9c/5844ab4ca6a4dd97a1850e030a15ec7d292b5c5cb93082979225126e35dd/coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520", size = 218302, upload-time = "2025-09-21T20:02:42.527Z" }, - { url = "https://files.pythonhosted.org/packages/f0/89/673f6514b0961d1f0e20ddc242e9342f6da21eaba3489901b565c0689f34/coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32", size = 218578, upload-time = "2025-09-21T20:02:44.468Z" }, - { url = "https://files.pythonhosted.org/packages/05/e8/261cae479e85232828fb17ad536765c88dd818c8470aca690b0ac6feeaa3/coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f", size = 249629, upload-time = "2025-09-21T20:02:46.503Z" }, - { url = "https://files.pythonhosted.org/packages/82/62/14ed6546d0207e6eda876434e3e8475a3e9adbe32110ce896c9e0c06bb9a/coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a", size = 252162, upload-time = "2025-09-21T20:02:48.689Z" }, - { url = "https://files.pythonhosted.org/packages/ff/49/07f00db9ac6478e4358165a08fb41b469a1b053212e8a00cb02f0d27a05f/coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360", size = 253517, upload-time = "2025-09-21T20:02:50.31Z" }, - { url = "https://files.pythonhosted.org/packages/a2/59/c5201c62dbf165dfbc91460f6dbbaa85a8b82cfa6131ac45d6c1bfb52deb/coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69", size = 249632, upload-time = "2025-09-21T20:02:51.971Z" }, - { url = "https://files.pythonhosted.org/packages/07/ae/5920097195291a51fb00b3a70b9bbd2edbfe3c84876a1762bd1ef1565ebc/coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14", size = 251520, upload-time = "2025-09-21T20:02:53.858Z" }, - { url = "https://files.pythonhosted.org/packages/b9/3c/a815dde77a2981f5743a60b63df31cb322c944843e57dbd579326625a413/coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe", size = 249455, upload-time = "2025-09-21T20:02:55.807Z" }, - { url = "https://files.pythonhosted.org/packages/aa/99/f5cdd8421ea656abefb6c0ce92556709db2265c41e8f9fc6c8ae0f7824c9/coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e", size = 249287, upload-time = "2025-09-21T20:02:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/c3/7a/e9a2da6a1fc5d007dd51fca083a663ab930a8c4d149c087732a5dbaa0029/coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd", size = 250946, upload-time = "2025-09-21T20:02:59.431Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5b/0b5799aa30380a949005a353715095d6d1da81927d6dbed5def2200a4e25/coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2", size = 221009, upload-time = "2025-09-21T20:03:01.324Z" }, - { url = "https://files.pythonhosted.org/packages/da/b0/e802fbb6eb746de006490abc9bb554b708918b6774b722bb3a0e6aa1b7de/coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681", size = 221804, upload-time = "2025-09-21T20:03:03.4Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e8/71d0c8e374e31f39e3389bb0bd19e527d46f00ea8571ec7ec8fd261d8b44/coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880", size = 220384, upload-time = "2025-09-21T20:03:05.111Z" }, - { url = "https://files.pythonhosted.org/packages/62/09/9a5608d319fa3eba7a2019addeacb8c746fb50872b57a724c9f79f146969/coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63", size = 219047, upload-time = "2025-09-21T20:03:06.795Z" }, - { url = "https://files.pythonhosted.org/packages/f5/6f/f58d46f33db9f2e3647b2d0764704548c184e6f5e014bef528b7f979ef84/coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2", size = 219266, upload-time = "2025-09-21T20:03:08.495Z" }, - { url = "https://files.pythonhosted.org/packages/74/5c/183ffc817ba68e0b443b8c934c8795553eb0c14573813415bd59941ee165/coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d", size = 260767, upload-time = "2025-09-21T20:03:10.172Z" }, - { url = "https://files.pythonhosted.org/packages/0f/48/71a8abe9c1ad7e97548835e3cc1adbf361e743e9d60310c5f75c9e7bf847/coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0", size = 262931, upload-time = "2025-09-21T20:03:11.861Z" }, - { url = "https://files.pythonhosted.org/packages/84/fd/193a8fb132acfc0a901f72020e54be5e48021e1575bb327d8ee1097a28fd/coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699", size = 265186, upload-time = "2025-09-21T20:03:13.539Z" }, - { url = "https://files.pythonhosted.org/packages/b1/8f/74ecc30607dd95ad50e3034221113ccb1c6d4e8085cc761134782995daae/coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9", size = 259470, upload-time = "2025-09-21T20:03:15.584Z" }, - { url = "https://files.pythonhosted.org/packages/0f/55/79ff53a769f20d71b07023ea115c9167c0bb56f281320520cf64c5298a96/coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f", size = 262626, upload-time = "2025-09-21T20:03:17.673Z" }, - { url = "https://files.pythonhosted.org/packages/88/e2/dac66c140009b61ac3fc13af673a574b00c16efdf04f9b5c740703e953c0/coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1", size = 260386, upload-time = "2025-09-21T20:03:19.36Z" }, - { url = "https://files.pythonhosted.org/packages/a2/f1/f48f645e3f33bb9ca8a496bc4a9671b52f2f353146233ebd7c1df6160440/coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0", size = 258852, upload-time = "2025-09-21T20:03:21.007Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3b/8442618972c51a7affeead957995cfa8323c0c9bcf8fa5a027421f720ff4/coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399", size = 261534, upload-time = "2025-09-21T20:03:23.12Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dc/101f3fa3a45146db0cb03f5b4376e24c0aac818309da23e2de0c75295a91/coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235", size = 221784, upload-time = "2025-09-21T20:03:24.769Z" }, - { url = "https://files.pythonhosted.org/packages/4c/a1/74c51803fc70a8a40d7346660379e144be772bab4ac7bb6e6b905152345c/coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d", size = 222905, upload-time = "2025-09-21T20:03:26.93Z" }, - { url = "https://files.pythonhosted.org/packages/12/65/f116a6d2127df30bcafbceef0302d8a64ba87488bf6f73a6d8eebf060873/coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a", size = 220922, upload-time = "2025-09-21T20:03:28.672Z" }, - { url = "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260", size = 209952, upload-time = "2025-09-21T20:03:53.918Z" }, +version = "7.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/38/ee22495420457259d2f3390309505ea98f98a5eed40901cf62196abad006/coverage-7.11.0.tar.gz", hash = "sha256:167bd504ac1ca2af7ff3b81d245dfea0292c5032ebef9d66cc08a7d28c1b8050", size = 811905, upload-time = "2025-10-15T15:15:08.542Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/7f/85e4dfe65e400645464b25c036a26ac226cf3a69d4a50c3934c532491cdd/coverage-7.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cc3f49e65ea6e0d5d9bd60368684fe52a704d46f9e7fc413918f18d046ec40e1", size = 216129, upload-time = "2025-10-15T15:13:25.371Z" }, + { url = "https://files.pythonhosted.org/packages/96/5d/dc5fa98fea3c175caf9d360649cb1aa3715e391ab00dc78c4c66fabd7356/coverage-7.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f39ae2f63f37472c17b4990f794035c9890418b1b8cca75c01193f3c8d3e01be", size = 216380, upload-time = "2025-10-15T15:13:26.976Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f5/3da9cc9596708273385189289c0e4d8197d37a386bdf17619013554b3447/coverage-7.11.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7db53b5cdd2917b6eaadd0b1251cf4e7d96f4a8d24e174bdbdf2f65b5ea7994d", size = 247375, upload-time = "2025-10-15T15:13:28.923Z" }, + { url = "https://files.pythonhosted.org/packages/65/6c/f7f59c342359a235559d2bc76b0c73cfc4bac7d61bb0df210965cb1ecffd/coverage-7.11.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10ad04ac3a122048688387828b4537bc9cf60c0bf4869c1e9989c46e45690b82", size = 249978, upload-time = "2025-10-15T15:13:30.525Z" }, + { url = "https://files.pythonhosted.org/packages/e7/8c/042dede2e23525e863bf1ccd2b92689692a148d8b5fd37c37899ba882645/coverage-7.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4036cc9c7983a2b1f2556d574d2eb2154ac6ed55114761685657e38782b23f52", size = 251253, upload-time = "2025-10-15T15:13:32.174Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a9/3c58df67bfa809a7bddd786356d9c5283e45d693edb5f3f55d0986dd905a/coverage-7.11.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ab934dd13b1c5e94b692b1e01bd87e4488cb746e3a50f798cb9464fd128374b", size = 247591, upload-time = "2025-10-15T15:13:34.147Z" }, + { url = "https://files.pythonhosted.org/packages/26/5b/c7f32efd862ee0477a18c41e4761305de6ddd2d49cdeda0c1116227570fd/coverage-7.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59a6e5a265f7cfc05f76e3bb53eca2e0dfe90f05e07e849930fecd6abb8f40b4", size = 249411, upload-time = "2025-10-15T15:13:38.425Z" }, + { url = "https://files.pythonhosted.org/packages/76/b5/78cb4f1e86c1611431c990423ec0768122905b03837e1b4c6a6f388a858b/coverage-7.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:df01d6c4c81e15a7c88337b795bb7595a8596e92310266b5072c7e301168efbd", size = 247303, upload-time = "2025-10-15T15:13:40.464Z" }, + { url = "https://files.pythonhosted.org/packages/87/c9/23c753a8641a330f45f221286e707c427e46d0ffd1719b080cedc984ec40/coverage-7.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8c934bd088eed6174210942761e38ee81d28c46de0132ebb1801dbe36a390dcc", size = 247157, upload-time = "2025-10-15T15:13:42.087Z" }, + { url = "https://files.pythonhosted.org/packages/c5/42/6e0cc71dc8a464486e944a4fa0d85bdec031cc2969e98ed41532a98336b9/coverage-7.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a03eaf7ec24078ad64a07f02e30060aaf22b91dedf31a6b24d0d98d2bba7f48", size = 248921, upload-time = "2025-10-15T15:13:43.715Z" }, + { url = "https://files.pythonhosted.org/packages/e8/1c/743c2ef665e6858cccb0f84377dfe3a4c25add51e8c7ef19249be92465b6/coverage-7.11.0-cp313-cp313-win32.whl", hash = "sha256:695340f698a5f56f795b2836abe6fb576e7c53d48cd155ad2f80fd24bc63a040", size = 218526, upload-time = "2025-10-15T15:13:45.336Z" }, + { url = "https://files.pythonhosted.org/packages/ff/d5/226daadfd1bf8ddbccefbd3aa3547d7b960fb48e1bdac124e2dd13a2b71a/coverage-7.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:2727d47fce3ee2bac648528e41455d1b0c46395a087a229deac75e9f88ba5a05", size = 219317, upload-time = "2025-10-15T15:13:47.401Z" }, + { url = "https://files.pythonhosted.org/packages/97/54/47db81dcbe571a48a298f206183ba8a7ba79200a37cd0d9f4788fcd2af4a/coverage-7.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:0efa742f431529699712b92ecdf22de8ff198df41e43aeaaadf69973eb93f17a", size = 217948, upload-time = "2025-10-15T15:13:49.096Z" }, + { url = "https://files.pythonhosted.org/packages/e5/8b/cb68425420154e7e2a82fd779a8cc01549b6fa83c2ad3679cd6c088ebd07/coverage-7.11.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:587c38849b853b157706407e9ebdca8fd12f45869edb56defbef2daa5fb0812b", size = 216837, upload-time = "2025-10-15T15:13:51.09Z" }, + { url = "https://files.pythonhosted.org/packages/33/55/9d61b5765a025685e14659c8d07037247de6383c0385757544ffe4606475/coverage-7.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b971bdefdd75096163dd4261c74be813c4508477e39ff7b92191dea19f24cd37", size = 217061, upload-time = "2025-10-15T15:13:52.747Z" }, + { url = "https://files.pythonhosted.org/packages/52/85/292459c9186d70dcec6538f06ea251bc968046922497377bf4a1dc9a71de/coverage-7.11.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:269bfe913b7d5be12ab13a95f3a76da23cf147be7fa043933320ba5625f0a8de", size = 258398, upload-time = "2025-10-15T15:13:54.45Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e2/46edd73fb8bf51446c41148d81944c54ed224854812b6ca549be25113ee0/coverage-7.11.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:dadbcce51a10c07b7c72b0ce4a25e4b6dcb0c0372846afb8e5b6307a121eb99f", size = 260574, upload-time = "2025-10-15T15:13:56.145Z" }, + { url = "https://files.pythonhosted.org/packages/07/5e/1df469a19007ff82e2ca8fe509822820a31e251f80ee7344c34f6cd2ec43/coverage-7.11.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ed43fa22c6436f7957df036331f8fe4efa7af132054e1844918866cd228af6c", size = 262797, upload-time = "2025-10-15T15:13:58.635Z" }, + { url = "https://files.pythonhosted.org/packages/f9/50/de216b31a1434b94d9b34a964c09943c6be45069ec704bfc379d8d89a649/coverage-7.11.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9516add7256b6713ec08359b7b05aeff8850c98d357784c7205b2e60aa2513fa", size = 257361, upload-time = "2025-10-15T15:14:00.409Z" }, + { url = "https://files.pythonhosted.org/packages/82/1e/3f9f8344a48111e152e0fd495b6fff13cc743e771a6050abf1627a7ba918/coverage-7.11.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb92e47c92fcbcdc692f428da67db33337fa213756f7adb6a011f7b5a7a20740", size = 260349, upload-time = "2025-10-15T15:14:02.188Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/3f52741f9e7d82124272f3070bbe316006a7de1bad1093f88d59bfc6c548/coverage-7.11.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d06f4fc7acf3cabd6d74941d53329e06bab00a8fe10e4df2714f0b134bfc64ef", size = 258114, upload-time = "2025-10-15T15:14:03.907Z" }, + { url = "https://files.pythonhosted.org/packages/0b/8b/918f0e15f0365d50d3986bbd3338ca01178717ac5678301f3f547b6619e6/coverage-7.11.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:6fbcee1a8f056af07ecd344482f711f563a9eb1c2cad192e87df00338ec3cdb0", size = 256723, upload-time = "2025-10-15T15:14:06.324Z" }, + { url = "https://files.pythonhosted.org/packages/44/9e/7776829f82d3cf630878a7965a7d70cc6ca94f22c7d20ec4944f7148cb46/coverage-7.11.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dbbf012be5f32533a490709ad597ad8a8ff80c582a95adc8d62af664e532f9ca", size = 259238, upload-time = "2025-10-15T15:14:08.002Z" }, + { url = "https://files.pythonhosted.org/packages/9a/b8/49cf253e1e7a3bedb85199b201862dd7ca4859f75b6cf25ffa7298aa0760/coverage-7.11.0-cp313-cp313t-win32.whl", hash = "sha256:cee6291bb4fed184f1c2b663606a115c743df98a537c969c3c64b49989da96c2", size = 219180, upload-time = "2025-10-15T15:14:09.786Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e1/1a541703826be7ae2125a0fb7f821af5729d56bb71e946e7b933cc7a89a4/coverage-7.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a386c1061bf98e7ea4758e4313c0ab5ecf57af341ef0f43a0bf26c2477b5c268", size = 220241, upload-time = "2025-10-15T15:14:11.471Z" }, + { url = "https://files.pythonhosted.org/packages/d5/d1/5ee0e0a08621140fd418ec4020f595b4d52d7eb429ae6a0c6542b4ba6f14/coverage-7.11.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f9ea02ef40bb83823b2b04964459d281688fe173e20643870bb5d2edf68bc836", size = 218510, upload-time = "2025-10-15T15:14:13.46Z" }, + { url = "https://files.pythonhosted.org/packages/f4/06/e923830c1985ce808e40a3fa3eb46c13350b3224b7da59757d37b6ce12b8/coverage-7.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c770885b28fb399aaf2a65bbd1c12bf6f307ffd112d6a76c5231a94276f0c497", size = 216110, upload-time = "2025-10-15T15:14:15.157Z" }, + { url = "https://files.pythonhosted.org/packages/42/82/cdeed03bfead45203fb651ed756dfb5266028f5f939e7f06efac4041dad5/coverage-7.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a3d0e2087dba64c86a6b254f43e12d264b636a39e88c5cc0a01a7c71bcfdab7e", size = 216395, upload-time = "2025-10-15T15:14:16.863Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ba/e1c80caffc3199aa699813f73ff097bc2df7b31642bdbc7493600a8f1de5/coverage-7.11.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73feb83bb41c32811973b8565f3705caf01d928d972b72042b44e97c71fd70d1", size = 247433, upload-time = "2025-10-15T15:14:18.589Z" }, + { url = "https://files.pythonhosted.org/packages/80/c0/5b259b029694ce0a5bbc1548834c7ba3db41d3efd3474489d7efce4ceb18/coverage-7.11.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c6f31f281012235ad08f9a560976cc2fc9c95c17604ff3ab20120fe480169bca", size = 249970, upload-time = "2025-10-15T15:14:20.307Z" }, + { url = "https://files.pythonhosted.org/packages/8c/86/171b2b5e1aac7e2fd9b43f7158b987dbeb95f06d1fbecad54ad8163ae3e8/coverage-7.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9570ad567f880ef675673992222746a124b9595506826b210fbe0ce3f0499cd", size = 251324, upload-time = "2025-10-15T15:14:22.419Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/7e10414d343385b92024af3932a27a1caf75c6e27ee88ba211221ff1a145/coverage-7.11.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8badf70446042553a773547a61fecaa734b55dc738cacf20c56ab04b77425e43", size = 247445, upload-time = "2025-10-15T15:14:24.205Z" }, + { url = "https://files.pythonhosted.org/packages/c4/3b/e4f966b21f5be8c4bf86ad75ae94efa0de4c99c7bbb8114476323102e345/coverage-7.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a09c1211959903a479e389685b7feb8a17f59ec5a4ef9afde7650bd5eabc2777", size = 249324, upload-time = "2025-10-15T15:14:26.234Z" }, + { url = "https://files.pythonhosted.org/packages/00/a2/8479325576dfcd909244d0df215f077f47437ab852ab778cfa2f8bf4d954/coverage-7.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:5ef83b107f50db3f9ae40f69e34b3bd9337456c5a7fe3461c7abf8b75dd666a2", size = 247261, upload-time = "2025-10-15T15:14:28.42Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d8/3a9e2db19d94d65771d0f2e21a9ea587d11b831332a73622f901157cc24b/coverage-7.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f91f927a3215b8907e214af77200250bb6aae36eca3f760f89780d13e495388d", size = 247092, upload-time = "2025-10-15T15:14:30.784Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b1/bbca3c472544f9e2ad2d5116b2379732957048be4b93a9c543fcd0207e5f/coverage-7.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbcd376716d6b7fbfeedd687a6c4be019c5a5671b35f804ba76a4c0a778cba4", size = 248755, upload-time = "2025-10-15T15:14:32.585Z" }, + { url = "https://files.pythonhosted.org/packages/89/49/638d5a45a6a0f00af53d6b637c87007eb2297042186334e9923a61aa8854/coverage-7.11.0-cp314-cp314-win32.whl", hash = "sha256:bab7ec4bb501743edc63609320aaec8cd9188b396354f482f4de4d40a9d10721", size = 218793, upload-time = "2025-10-15T15:14:34.972Z" }, + { url = "https://files.pythonhosted.org/packages/30/cc/b675a51f2d068adb3cdf3799212c662239b0ca27f4691d1fff81b92ea850/coverage-7.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:3d4ba9a449e9364a936a27322b20d32d8b166553bfe63059bd21527e681e2fad", size = 219587, upload-time = "2025-10-15T15:14:37.047Z" }, + { url = "https://files.pythonhosted.org/packages/93/98/5ac886876026de04f00820e5094fe22166b98dcb8b426bf6827aaf67048c/coverage-7.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:ce37f215223af94ef0f75ac68ea096f9f8e8c8ec7d6e8c346ee45c0d363f0479", size = 218168, upload-time = "2025-10-15T15:14:38.861Z" }, + { url = "https://files.pythonhosted.org/packages/14/d1/b4145d35b3e3ecf4d917e97fc8895bcf027d854879ba401d9ff0f533f997/coverage-7.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:f413ce6e07e0d0dc9c433228727b619871532674b45165abafe201f200cc215f", size = 216850, upload-time = "2025-10-15T15:14:40.651Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d1/7f645fc2eccd318369a8a9948acc447bb7c1ade2911e31d3c5620544c22b/coverage-7.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:05791e528a18f7072bf5998ba772fe29db4da1234c45c2087866b5ba4dea710e", size = 217071, upload-time = "2025-10-15T15:14:42.755Z" }, + { url = "https://files.pythonhosted.org/packages/54/7d/64d124649db2737ceced1dfcbdcb79898d5868d311730f622f8ecae84250/coverage-7.11.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cacb29f420cfeb9283b803263c3b9a068924474ff19ca126ba9103e1278dfa44", size = 258570, upload-time = "2025-10-15T15:14:44.542Z" }, + { url = "https://files.pythonhosted.org/packages/6c/3f/6f5922f80dc6f2d8b2c6f974835c43f53eb4257a7797727e6ca5b7b2ec1f/coverage-7.11.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314c24e700d7027ae3ab0d95fbf8d53544fca1f20345fd30cd219b737c6e58d3", size = 260738, upload-time = "2025-10-15T15:14:46.436Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5f/9e883523c4647c860b3812b417a2017e361eca5b635ee658387dc11b13c1/coverage-7.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:630d0bd7a293ad2fc8b4b94e5758c8b2536fdf36c05f1681270203e463cbfa9b", size = 262994, upload-time = "2025-10-15T15:14:48.3Z" }, + { url = "https://files.pythonhosted.org/packages/07/bb/43b5a8e94c09c8bf51743ffc65c4c841a4ca5d3ed191d0a6919c379a1b83/coverage-7.11.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e89641f5175d65e2dbb44db15fe4ea48fade5d5bbb9868fdc2b4fce22f4a469d", size = 257282, upload-time = "2025-10-15T15:14:50.236Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e5/0ead8af411411330b928733e1d201384b39251a5f043c1612970310e8283/coverage-7.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c9f08ea03114a637dab06cedb2e914da9dc67fa52c6015c018ff43fdde25b9c2", size = 260430, upload-time = "2025-10-15T15:14:52.413Z" }, + { url = "https://files.pythonhosted.org/packages/ae/66/03dd8bb0ba5b971620dcaac145461950f6d8204953e535d2b20c6b65d729/coverage-7.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce9f3bde4e9b031eaf1eb61df95c1401427029ea1bfddb8621c1161dcb0fa02e", size = 258190, upload-time = "2025-10-15T15:14:54.268Z" }, + { url = "https://files.pythonhosted.org/packages/45/ae/28a9cce40bf3174426cb2f7e71ee172d98e7f6446dff936a7ccecee34b14/coverage-7.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:e4dc07e95495923d6fd4d6c27bf70769425b71c89053083843fd78f378558996", size = 256658, upload-time = "2025-10-15T15:14:56.436Z" }, + { url = "https://files.pythonhosted.org/packages/5c/7c/3a44234a8599513684bfc8684878fd7b126c2760f79712bb78c56f19efc4/coverage-7.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:424538266794db2861db4922b05d729ade0940ee69dcf0591ce8f69784db0e11", size = 259342, upload-time = "2025-10-15T15:14:58.538Z" }, + { url = "https://files.pythonhosted.org/packages/e1/e6/0108519cba871af0351725ebdb8660fd7a0fe2ba3850d56d32490c7d9b4b/coverage-7.11.0-cp314-cp314t-win32.whl", hash = "sha256:4c1eeb3fb8eb9e0190bebafd0462936f75717687117339f708f395fe455acc73", size = 219568, upload-time = "2025-10-15T15:15:00.382Z" }, + { url = "https://files.pythonhosted.org/packages/c9/76/44ba876e0942b4e62fdde23ccb029ddb16d19ba1bef081edd00857ba0b16/coverage-7.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b56efee146c98dbf2cf5cffc61b9829d1e94442df4d7398b26892a53992d3547", size = 220687, upload-time = "2025-10-15T15:15:02.322Z" }, + { url = "https://files.pythonhosted.org/packages/b9/0c/0df55ecb20d0d0ed5c322e10a441775e1a3a5d78c60f0c4e1abfe6fcf949/coverage-7.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b5c2705afa83f49bd91962a4094b6b082f94aef7626365ab3f8f4bd159c5acf3", size = 218711, upload-time = "2025-10-15T15:15:04.575Z" }, + { url = "https://files.pythonhosted.org/packages/5f/04/642c1d8a448ae5ea1369eac8495740a79eb4e581a9fb0cbdce56bbf56da1/coverage-7.11.0-py3-none-any.whl", hash = "sha256:4b7589765348d78fb4e5fb6ea35d07564e387da2fc5efff62e0222971f155f68", size = 207761, upload-time = "2025-10-15T15:15:06.439Z" }, ] [[package]] name = "cryptography" -version = "46.0.2" +version = "46.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4a/9b/e301418629f7bfdf72db9e80ad6ed9d1b83c487c471803eaa6464c511a01/cryptography-46.0.2.tar.gz", hash = "sha256:21b6fc8c71a3f9a604f028a329e5560009cc4a3a828bfea5fcba8eb7647d88fe", size = 749293, upload-time = "2025-10-01T00:29:11.856Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/98/7a8df8c19a335c8028414738490fc3955c0cecbfdd37fcc1b9c3d04bd561/cryptography-46.0.2-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:f3e32ab7dd1b1ef67b9232c4cf5e2ee4cd517d4316ea910acaaa9c5712a1c663", size = 7261255, upload-time = "2025-10-01T00:27:22.947Z" }, - { url = "https://files.pythonhosted.org/packages/c6/38/b2adb2aa1baa6706adc3eb746691edd6f90a656a9a65c3509e274d15a2b8/cryptography-46.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1fd1a69086926b623ef8126b4c33d5399ce9e2f3fac07c9c734c2a4ec38b6d02", size = 4297596, upload-time = "2025-10-01T00:27:25.258Z" }, - { url = "https://files.pythonhosted.org/packages/e4/27/0f190ada240003119488ae66c897b5e97149292988f556aef4a6a2a57595/cryptography-46.0.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb7fb9cd44c2582aa5990cf61a4183e6f54eea3172e54963787ba47287edd135", size = 4450899, upload-time = "2025-10-01T00:27:27.458Z" }, - { url = "https://files.pythonhosted.org/packages/85/d5/e4744105ab02fdf6bb58ba9a816e23b7a633255987310b4187d6745533db/cryptography-46.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9066cfd7f146f291869a9898b01df1c9b0e314bfa182cef432043f13fc462c92", size = 4300382, upload-time = "2025-10-01T00:27:29.091Z" }, - { url = "https://files.pythonhosted.org/packages/33/fb/bf9571065c18c04818cb07de90c43fc042c7977c68e5de6876049559c72f/cryptography-46.0.2-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:97e83bf4f2f2c084d8dd792d13841d0a9b241643151686010866bbd076b19659", size = 4017347, upload-time = "2025-10-01T00:27:30.767Z" }, - { url = "https://files.pythonhosted.org/packages/35/72/fc51856b9b16155ca071080e1a3ad0c3a8e86616daf7eb018d9565b99baa/cryptography-46.0.2-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:4a766d2a5d8127364fd936572c6e6757682fc5dfcbdba1632d4554943199f2fa", size = 4983500, upload-time = "2025-10-01T00:27:32.741Z" }, - { url = "https://files.pythonhosted.org/packages/c1/53/0f51e926799025e31746d454ab2e36f8c3f0d41592bc65cb9840368d3275/cryptography-46.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:fab8f805e9675e61ed8538f192aad70500fa6afb33a8803932999b1049363a08", size = 4482591, upload-time = "2025-10-01T00:27:34.869Z" }, - { url = "https://files.pythonhosted.org/packages/86/96/4302af40b23ab8aa360862251fb8fc450b2a06ff24bc5e261c2007f27014/cryptography-46.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:1e3b6428a3d56043bff0bb85b41c535734204e599c1c0977e1d0f261b02f3ad5", size = 4300019, upload-time = "2025-10-01T00:27:37.029Z" }, - { url = "https://files.pythonhosted.org/packages/9b/59/0be12c7fcc4c5e34fe2b665a75bc20958473047a30d095a7657c218fa9e8/cryptography-46.0.2-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:1a88634851d9b8de8bb53726f4300ab191d3b2f42595e2581a54b26aba71b7cc", size = 4950006, upload-time = "2025-10-01T00:27:40.272Z" }, - { url = "https://files.pythonhosted.org/packages/55/1d/42fda47b0111834b49e31590ae14fd020594d5e4dadd639bce89ad790fba/cryptography-46.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:be939b99d4e091eec9a2bcf41aaf8f351f312cd19ff74b5c83480f08a8a43e0b", size = 4482088, upload-time = "2025-10-01T00:27:42.668Z" }, - { url = "https://files.pythonhosted.org/packages/17/50/60f583f69aa1602c2bdc7022dae86a0d2b837276182f8c1ec825feb9b874/cryptography-46.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f13b040649bc18e7eb37936009b24fd31ca095a5c647be8bb6aaf1761142bd1", size = 4425599, upload-time = "2025-10-01T00:27:44.616Z" }, - { url = "https://files.pythonhosted.org/packages/d1/57/d8d4134cd27e6e94cf44adb3f3489f935bde85f3a5508e1b5b43095b917d/cryptography-46.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bdc25e4e01b261a8fda4e98618f1c9515febcecebc9566ddf4a70c63967043b", size = 4697458, upload-time = "2025-10-01T00:27:46.209Z" }, - { url = "https://files.pythonhosted.org/packages/d1/2b/531e37408573e1da33adfb4c58875013ee8ac7d548d1548967d94a0ae5c4/cryptography-46.0.2-cp311-abi3-win32.whl", hash = "sha256:8b9bf67b11ef9e28f4d78ff88b04ed0929fcd0e4f70bb0f704cfc32a5c6311ee", size = 3056077, upload-time = "2025-10-01T00:27:48.424Z" }, - { url = "https://files.pythonhosted.org/packages/a8/cd/2f83cafd47ed2dc5a3a9c783ff5d764e9e70d3a160e0df9a9dcd639414ce/cryptography-46.0.2-cp311-abi3-win_amd64.whl", hash = "sha256:758cfc7f4c38c5c5274b55a57ef1910107436f4ae842478c4989abbd24bd5acb", size = 3512585, upload-time = "2025-10-01T00:27:50.521Z" }, - { url = "https://files.pythonhosted.org/packages/00/36/676f94e10bfaa5c5b86c469ff46d3e0663c5dc89542f7afbadac241a3ee4/cryptography-46.0.2-cp311-abi3-win_arm64.whl", hash = "sha256:218abd64a2e72f8472c2102febb596793347a3e65fafbb4ad50519969da44470", size = 2927474, upload-time = "2025-10-01T00:27:52.91Z" }, - { url = "https://files.pythonhosted.org/packages/6f/cc/47fc6223a341f26d103cb6da2216805e08a37d3b52bee7f3b2aee8066f95/cryptography-46.0.2-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:bda55e8dbe8533937956c996beaa20266a8eca3570402e52ae52ed60de1faca8", size = 7198626, upload-time = "2025-10-01T00:27:54.8Z" }, - { url = "https://files.pythonhosted.org/packages/93/22/d66a8591207c28bbe4ac7afa25c4656dc19dc0db29a219f9809205639ede/cryptography-46.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e7155c0b004e936d381b15425273aee1cebc94f879c0ce82b0d7fecbf755d53a", size = 4287584, upload-time = "2025-10-01T00:27:57.018Z" }, - { url = "https://files.pythonhosted.org/packages/8c/3e/fac3ab6302b928e0398c269eddab5978e6c1c50b2b77bb5365ffa8633b37/cryptography-46.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a61c154cc5488272a6c4b86e8d5beff4639cdb173d75325ce464d723cda0052b", size = 4433796, upload-time = "2025-10-01T00:27:58.631Z" }, - { url = "https://files.pythonhosted.org/packages/7d/d8/24392e5d3c58e2d83f98fe5a2322ae343360ec5b5b93fe18bc52e47298f5/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:9ec3f2e2173f36a9679d3b06d3d01121ab9b57c979de1e6a244b98d51fea1b20", size = 4292126, upload-time = "2025-10-01T00:28:00.643Z" }, - { url = "https://files.pythonhosted.org/packages/ed/38/3d9f9359b84c16c49a5a336ee8be8d322072a09fac17e737f3bb11f1ce64/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2fafb6aa24e702bbf74de4cb23bfa2c3beb7ab7683a299062b69724c92e0fa73", size = 3993056, upload-time = "2025-10-01T00:28:02.8Z" }, - { url = "https://files.pythonhosted.org/packages/d6/a3/4c44fce0d49a4703cc94bfbe705adebf7ab36efe978053742957bc7ec324/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:0c7ffe8c9b1fcbb07a26d7c9fa5e857c2fe80d72d7b9e0353dcf1d2180ae60ee", size = 4967604, upload-time = "2025-10-01T00:28:04.783Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c2/49d73218747c8cac16bb8318a5513fde3129e06a018af3bc4dc722aa4a98/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:5840f05518caa86b09d23f8b9405a7b6d5400085aa14a72a98fdf5cf1568c0d2", size = 4465367, upload-time = "2025-10-01T00:28:06.864Z" }, - { url = "https://files.pythonhosted.org/packages/1b/64/9afa7d2ee742f55ca6285a54386ed2778556a4ed8871571cb1c1bfd8db9e/cryptography-46.0.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:27c53b4f6a682a1b645fbf1cd5058c72cf2f5aeba7d74314c36838c7cbc06e0f", size = 4291678, upload-time = "2025-10-01T00:28:08.982Z" }, - { url = "https://files.pythonhosted.org/packages/50/48/1696d5ea9623a7b72ace87608f6899ca3c331709ac7ebf80740abb8ac673/cryptography-46.0.2-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:512c0250065e0a6b286b2db4bbcc2e67d810acd53eb81733e71314340366279e", size = 4931366, upload-time = "2025-10-01T00:28:10.74Z" }, - { url = "https://files.pythonhosted.org/packages/eb/3c/9dfc778401a334db3b24435ee0733dd005aefb74afe036e2d154547cb917/cryptography-46.0.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:07c0eb6657c0e9cca5891f4e35081dbf985c8131825e21d99b4f440a8f496f36", size = 4464738, upload-time = "2025-10-01T00:28:12.491Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b1/abcde62072b8f3fd414e191a6238ce55a0050e9738090dc6cded24c12036/cryptography-46.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:48b983089378f50cba258f7f7aa28198c3f6e13e607eaf10472c26320332ca9a", size = 4419305, upload-time = "2025-10-01T00:28:14.145Z" }, - { url = "https://files.pythonhosted.org/packages/c7/1f/3d2228492f9391395ca34c677e8f2571fb5370fe13dc48c1014f8c509864/cryptography-46.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e6f6775eaaa08c0eec73e301f7592f4367ccde5e4e4df8e58320f2ebf161ea2c", size = 4681201, upload-time = "2025-10-01T00:28:15.951Z" }, - { url = "https://files.pythonhosted.org/packages/de/77/b687745804a93a55054f391528fcfc76c3d6bfd082ce9fb62c12f0d29fc1/cryptography-46.0.2-cp314-cp314t-win32.whl", hash = "sha256:e8633996579961f9b5a3008683344c2558d38420029d3c0bc7ff77c17949a4e1", size = 3022492, upload-time = "2025-10-01T00:28:17.643Z" }, - { url = "https://files.pythonhosted.org/packages/60/a5/8d498ef2996e583de0bef1dcc5e70186376f00883ae27bf2133f490adf21/cryptography-46.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:48c01988ecbb32979bb98731f5c2b2f79042a6c58cc9a319c8c2f9987c7f68f9", size = 3496215, upload-time = "2025-10-01T00:28:19.272Z" }, - { url = "https://files.pythonhosted.org/packages/56/db/ee67aaef459a2706bc302b15889a1a8126ebe66877bab1487ae6ad00f33d/cryptography-46.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:8e2ad4d1a5899b7caa3a450e33ee2734be7cc0689010964703a7c4bcc8dd4fd0", size = 2919255, upload-time = "2025-10-01T00:28:21.115Z" }, - { url = "https://files.pythonhosted.org/packages/d5/bb/fa95abcf147a1b0bb94d95f53fbb09da77b24c776c5d87d36f3d94521d2c/cryptography-46.0.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a08e7401a94c002e79dc3bc5231b6558cd4b2280ee525c4673f650a37e2c7685", size = 7248090, upload-time = "2025-10-01T00:28:22.846Z" }, - { url = "https://files.pythonhosted.org/packages/b7/66/f42071ce0e3ffbfa80a88feadb209c779fda92a23fbc1e14f74ebf72ef6b/cryptography-46.0.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d30bc11d35743bf4ddf76674a0a369ec8a21f87aaa09b0661b04c5f6c46e8d7b", size = 4293123, upload-time = "2025-10-01T00:28:25.072Z" }, - { url = "https://files.pythonhosted.org/packages/a8/5d/1fdbd2e5c1ba822828d250e5a966622ef00185e476d1cd2726b6dd135e53/cryptography-46.0.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bca3f0ce67e5a2a2cf524e86f44697c4323a86e0fd7ba857de1c30d52c11ede1", size = 4439524, upload-time = "2025-10-01T00:28:26.808Z" }, - { url = "https://files.pythonhosted.org/packages/c8/c1/5e4989a7d102d4306053770d60f978c7b6b1ea2ff8c06e0265e305b23516/cryptography-46.0.2-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ff798ad7a957a5021dcbab78dfff681f0cf15744d0e6af62bd6746984d9c9e9c", size = 4297264, upload-time = "2025-10-01T00:28:29.327Z" }, - { url = "https://files.pythonhosted.org/packages/28/78/b56f847d220cb1d6d6aef5a390e116ad603ce13a0945a3386a33abc80385/cryptography-46.0.2-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:cb5e8daac840e8879407acbe689a174f5ebaf344a062f8918e526824eb5d97af", size = 4011872, upload-time = "2025-10-01T00:28:31.479Z" }, - { url = "https://files.pythonhosted.org/packages/e1/80/2971f214b066b888944f7b57761bf709ee3f2cf805619a18b18cab9b263c/cryptography-46.0.2-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:3f37aa12b2d91e157827d90ce78f6180f0c02319468a0aea86ab5a9566da644b", size = 4978458, upload-time = "2025-10-01T00:28:33.267Z" }, - { url = "https://files.pythonhosted.org/packages/a5/84/0cb0a2beaa4f1cbe63ebec4e97cd7e0e9f835d0ba5ee143ed2523a1e0016/cryptography-46.0.2-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e38f203160a48b93010b07493c15f2babb4e0f2319bbd001885adb3f3696d21", size = 4472195, upload-time = "2025-10-01T00:28:36.039Z" }, - { url = "https://files.pythonhosted.org/packages/30/8b/2b542ddbf78835c7cd67b6fa79e95560023481213a060b92352a61a10efe/cryptography-46.0.2-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d19f5f48883752b5ab34cff9e2f7e4a7f216296f33714e77d1beb03d108632b6", size = 4296791, upload-time = "2025-10-01T00:28:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/78/12/9065b40201b4f4876e93b9b94d91feb18de9150d60bd842a16a21565007f/cryptography-46.0.2-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:04911b149eae142ccd8c9a68892a70c21613864afb47aba92d8c7ed9cc001023", size = 4939629, upload-time = "2025-10-01T00:28:39.654Z" }, - { url = "https://files.pythonhosted.org/packages/f6/9e/6507dc048c1b1530d372c483dfd34e7709fc542765015425f0442b08547f/cryptography-46.0.2-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:8b16c1ede6a937c291d41176934268e4ccac2c6521c69d3f5961c5a1e11e039e", size = 4471988, upload-time = "2025-10-01T00:28:41.822Z" }, - { url = "https://files.pythonhosted.org/packages/b1/86/d025584a5f7d5c5ec8d3633dbcdce83a0cd579f1141ceada7817a4c26934/cryptography-46.0.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:747b6f4a4a23d5a215aadd1d0b12233b4119c4313df83ab4137631d43672cc90", size = 4422989, upload-time = "2025-10-01T00:28:43.608Z" }, - { url = "https://files.pythonhosted.org/packages/4b/39/536370418b38a15a61bbe413006b79dfc3d2b4b0eafceb5581983f973c15/cryptography-46.0.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6b275e398ab3a7905e168c036aad54b5969d63d3d9099a0a66cc147a3cc983be", size = 4685578, upload-time = "2025-10-01T00:28:45.361Z" }, - { url = "https://files.pythonhosted.org/packages/15/52/ea7e2b1910f547baed566c866fbb86de2402e501a89ecb4871ea7f169a81/cryptography-46.0.2-cp38-abi3-win32.whl", hash = "sha256:0b507c8e033307e37af61cb9f7159b416173bdf5b41d11c4df2e499a1d8e007c", size = 3036711, upload-time = "2025-10-01T00:28:47.096Z" }, - { url = "https://files.pythonhosted.org/packages/71/9e/171f40f9c70a873e73c2efcdbe91e1d4b1777a03398fa1c4af3c56a2477a/cryptography-46.0.2-cp38-abi3-win_amd64.whl", hash = "sha256:f9b2dc7668418fb6f221e4bf701f716e05e8eadb4f1988a2487b11aedf8abe62", size = 3500007, upload-time = "2025-10-01T00:28:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/3e/7c/15ad426257615f9be8caf7f97990cf3dcbb5b8dd7ed7e0db581a1c4759dd/cryptography-46.0.2-cp38-abi3-win_arm64.whl", hash = "sha256:91447f2b17e83c9e0c89f133119d83f94ce6e0fb55dd47da0a959316e6e9cfa1", size = 2918153, upload-time = "2025-10-01T00:28:51.003Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" }, + { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" }, + { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" }, + { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" }, + { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" }, + { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" }, + { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" }, + { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" }, + { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" }, + { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" }, + { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" }, + { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" }, + { url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" }, + { url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" }, + { url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" }, + { url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" }, + { url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" }, + { url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" }, + { url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" }, + { url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" }, + { url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" }, + { url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" }, + { url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" }, + { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" }, + { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" }, + { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" }, + { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" }, + { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" }, + { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" }, + { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" }, + { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" }, + { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" }, + { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" }, + { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" }, ] [[package]] @@ -537,16 +537,16 @@ wheels = [ [[package]] name = "fastapi" -version = "0.118.2" +version = "0.119.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/ad/31a59efecca3b584440cafac6f69634f4661295c858912c2b2905280a089/fastapi-0.118.2.tar.gz", hash = "sha256:d5388dbe76d97cb6ccd2c93b4dd981608062ebf6335280edfa9a11af82443e18", size = 311963, upload-time = "2025-10-08T14:52:17.796Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f4/152127681182e6413e7a89684c434e19e7414ed7ac0c632999c3c6980640/fastapi-0.119.1.tar.gz", hash = "sha256:a5e3426edce3fe221af4e1992c6d79011b247e3b03cc57999d697fe76cbf8ae0", size = 338616, upload-time = "2025-10-20T11:30:27.734Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/7c/97d033faf771c9fe960c7b51eb78ab266bfa64cbc917601978963f0c3c7b/fastapi-0.118.2-py3-none-any.whl", hash = "sha256:d1f842612e6a305f95abe784b7f8d3215477742e7c67a16fccd20bd79db68150", size = 97954, upload-time = "2025-10-08T14:52:16.166Z" }, + { url = "https://files.pythonhosted.org/packages/b1/26/e6d959b4ac959fdb3e9c4154656fc160794db6af8e64673d52759456bf07/fastapi-0.119.1-py3-none-any.whl", hash = "sha256:0b8c2a2cce853216e150e9bd4faaed88227f8eb37de21cb200771f491586a27f", size = 108123, upload-time = "2025-10-20T11:30:26.185Z" }, ] [package.optional-dependencies] @@ -561,16 +561,16 @@ standard = [ [[package]] name = "fastapi-cli" -version = "0.0.13" +version = "0.0.14" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "rich-toolkit" }, { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/32/4e/3f61850012473b097fc5297d681bd85788e186fadb8555b67baf4c7707f4/fastapi_cli-0.0.13.tar.gz", hash = "sha256:312addf3f57ba7139457cf0d345c03e2170cc5a034057488259c33cd7e494529", size = 17780, upload-time = "2025-09-20T16:37:31.089Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/13/11e43d630be84e51ba5510a6da6a11eb93b44b72caa796137c5dddda937b/fastapi_cli-0.0.14.tar.gz", hash = "sha256:ddfb5de0a67f77a8b3271af1460489bd4d7f4add73d11fbfac613827b0275274", size = 17994, upload-time = "2025-10-20T16:33:21.054Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/36/7432750f3638324b055496d2c952000bea824259fca70df5577a6a3c172f/fastapi_cli-0.0.13-py3-none-any.whl", hash = "sha256:219b73ccfde7622559cef1d43197da928516acb4f21f2ec69128c4b90057baba", size = 11142, upload-time = "2025-09-20T16:37:29.695Z" }, + { url = "https://files.pythonhosted.org/packages/40/e8/bc8bbfd93dcc8e347ce98a3e654fb0d2e5f2739afb46b98f41a30c339269/fastapi_cli-0.0.14-py3-none-any.whl", hash = "sha256:e66b9ad499ee77a4e6007545cde6de1459b7f21df199d7f29aad2adaab168eca", size = 11151, upload-time = "2025-10-20T16:33:19.318Z" }, ] [package.optional-dependencies] @@ -711,7 +711,7 @@ wheels = [ [[package]] name = "google-api-python-client" -version = "2.184.0" +version = "2.185.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, @@ -720,9 +720,9 @@ dependencies = [ { name = "httplib2" }, { name = "uritemplate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7c/30/8b3a626ccf84ca43da62d77e2d40d70bedc6387951cc5104011cddce34e0/google_api_python_client-2.184.0.tar.gz", hash = "sha256:ef2a3330ad058cdfc8a558d199c051c3356f6ed012436c3ad3d08b67891b039f", size = 13694120, upload-time = "2025-10-01T21:13:48.961Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5a/6f9b49d67ea91376305fdb8bbf2877c746d756e45fd8fb7d2e32d6dad19b/google_api_python_client-2.185.0.tar.gz", hash = "sha256:aa1b338e4bb0f141c2df26743f6b46b11f38705aacd775b61971cbc51da089c3", size = 13885609, upload-time = "2025-10-17T15:00:35.623Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/38/d25ae1565103a545cf18207a5dec09a6d39ad88e5b0399a2430e9edb0550/google_api_python_client-2.184.0-py3-none-any.whl", hash = "sha256:15a18d02f42de99416921c77be235d12ead474e474a1abc348b01a2b92633fa4", size = 14260480, upload-time = "2025-10-01T21:13:46.037Z" }, + { url = "https://files.pythonhosted.org/packages/fa/28/be3b17bd6a190c8c2ec9e4fb65d43e6ecd7b7a1bb19ccc1d9ab4f687a58c/google_api_python_client-2.185.0-py3-none-any.whl", hash = "sha256:00fe173a4b346d2397fbe0d37ac15368170dfbed91a0395a66ef2558e22b93fc", size = 14453595, upload-time = "2025-10-17T15:00:33.176Z" }, ] [[package]] @@ -754,14 +754,14 @@ wheels = [ [[package]] name = "googleapis-common-protos" -version = "1.70.0" +version = "1.71.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903, upload-time = "2025-04-14T10:17:02.924Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/43/b25abe02db2911397819003029bef768f68a974f2ece483e6084d1a5f754/googleapis_common_protos-1.71.0.tar.gz", hash = "sha256:1aec01e574e29da63c80ba9f7bbf1ccfaacf1da877f23609fe236ca7c72a2e2e", size = 146454, upload-time = "2025-10-20T14:58:08.732Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530, upload-time = "2025-04-14T10:17:01.271Z" }, + { url = "https://files.pythonhosted.org/packages/25/e8/eba9fece11d57a71e3e22ea672742c8f3cf23b35730c9e96db768b295216/googleapis_common_protos-1.71.0-py3-none-any.whl", hash = "sha256:59034a1d849dc4d18971997a72ac56246570afdd17f9369a0ff68218d50ab78c", size = 294576, upload-time = "2025-10-20T14:56:21.295Z" }, ] [[package]] @@ -894,11 +894,11 @@ wheels = [ [[package]] name = "iniconfig" -version = "2.1.0" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +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/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, + { 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]] @@ -1045,54 +1045,54 @@ wheels = [ [[package]] name = "numpy" -version = "2.3.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/b9/984c2b1ee61a8b803bf63582b4ac4242cf76e2dbd663efeafcb620cc0ccb/numpy-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f5415fb78995644253370985342cd03572ef8620b934da27d77377a2285955bf", size = 20949588, upload-time = "2025-09-09T15:56:59.087Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e4/07970e3bed0b1384d22af1e9912527ecbeb47d3b26e9b6a3bced068b3bea/numpy-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d00de139a3324e26ed5b95870ce63be7ec7352171bc69a4cf1f157a48e3eb6b7", size = 14177802, upload-time = "2025-09-09T15:57:01.73Z" }, - { url = "https://files.pythonhosted.org/packages/35/c7/477a83887f9de61f1203bad89cf208b7c19cc9fef0cebef65d5a1a0619f2/numpy-2.3.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9dc13c6a5829610cc07422bc74d3ac083bd8323f14e2827d992f9e52e22cd6a6", size = 5106537, upload-time = "2025-09-09T15:57:03.765Z" }, - { url = "https://files.pythonhosted.org/packages/52/47/93b953bd5866a6f6986344d045a207d3f1cfbad99db29f534ea9cee5108c/numpy-2.3.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d79715d95f1894771eb4e60fb23f065663b2298f7d22945d66877aadf33d00c7", size = 6640743, upload-time = "2025-09-09T15:57:07.921Z" }, - { url = "https://files.pythonhosted.org/packages/23/83/377f84aaeb800b64c0ef4de58b08769e782edcefa4fea712910b6f0afd3c/numpy-2.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952cfd0748514ea7c3afc729a0fc639e61655ce4c55ab9acfab14bda4f402b4c", size = 14278881, upload-time = "2025-09-09T15:57:11.349Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b83648633d46f77039c29078751f80da65aa64d5622a3cd62aaef9d835b6c93", size = 16636301, upload-time = "2025-09-09T15:57:14.245Z" }, - { url = "https://files.pythonhosted.org/packages/a2/59/1287924242eb4fa3f9b3a2c30400f2e17eb2707020d1c5e3086fe7330717/numpy-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b001bae8cea1c7dfdb2ae2b017ed0a6f2102d7a70059df1e338e307a4c78a8ae", size = 16053645, upload-time = "2025-09-09T15:57:16.534Z" }, - { url = "https://files.pythonhosted.org/packages/e6/93/b3d47ed882027c35e94ac2320c37e452a549f582a5e801f2d34b56973c97/numpy-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e9aced64054739037d42fb84c54dd38b81ee238816c948c8f3ed134665dcd86", size = 18578179, upload-time = "2025-09-09T15:57:18.883Z" }, - { url = "https://files.pythonhosted.org/packages/20/d9/487a2bccbf7cc9d4bfc5f0f197761a5ef27ba870f1e3bbb9afc4bbe3fcc2/numpy-2.3.3-cp313-cp313-win32.whl", hash = "sha256:9591e1221db3f37751e6442850429b3aabf7026d3b05542d102944ca7f00c8a8", size = 6312250, upload-time = "2025-09-09T15:57:21.296Z" }, - { url = "https://files.pythonhosted.org/packages/1b/b5/263ebbbbcede85028f30047eab3d58028d7ebe389d6493fc95ae66c636ab/numpy-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f0dadeb302887f07431910f67a14d57209ed91130be0adea2f9793f1a4f817cf", size = 12783269, upload-time = "2025-09-09T15:57:23.034Z" }, - { url = "https://files.pythonhosted.org/packages/fa/75/67b8ca554bbeaaeb3fac2e8bce46967a5a06544c9108ec0cf5cece559b6c/numpy-2.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:3c7cf302ac6e0b76a64c4aecf1a09e51abd9b01fc7feee80f6c43e3ab1b1dbc5", size = 10195314, upload-time = "2025-09-09T15:57:25.045Z" }, - { url = "https://files.pythonhosted.org/packages/11/d0/0d1ddec56b162042ddfafeeb293bac672de9b0cfd688383590090963720a/numpy-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eda59e44957d272846bb407aad19f89dc6f58fecf3504bd144f4c5cf81a7eacc", size = 21048025, upload-time = "2025-09-09T15:57:27.257Z" }, - { url = "https://files.pythonhosted.org/packages/36/9e/1996ca6b6d00415b6acbdd3c42f7f03ea256e2c3f158f80bd7436a8a19f3/numpy-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:823d04112bc85ef5c4fda73ba24e6096c8f869931405a80aa8b0e604510a26bc", size = 14301053, upload-time = "2025-09-09T15:57:30.077Z" }, - { url = "https://files.pythonhosted.org/packages/05/24/43da09aa764c68694b76e84b3d3f0c44cb7c18cdc1ba80e48b0ac1d2cd39/numpy-2.3.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:40051003e03db4041aa325da2a0971ba41cf65714e65d296397cc0e32de6018b", size = 5229444, upload-time = "2025-09-09T15:57:32.733Z" }, - { url = "https://files.pythonhosted.org/packages/bc/14/50ffb0f22f7218ef8af28dd089f79f68289a7a05a208db9a2c5dcbe123c1/numpy-2.3.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ee9086235dd6ab7ae75aba5662f582a81ced49f0f1c6de4260a78d8f2d91a19", size = 6738039, upload-time = "2025-09-09T15:57:34.328Z" }, - { url = "https://files.pythonhosted.org/packages/55/52/af46ac0795e09657d45a7f4db961917314377edecf66db0e39fa7ab5c3d3/numpy-2.3.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94fcaa68757c3e2e668ddadeaa86ab05499a70725811e582b6a9858dd472fb30", size = 14352314, upload-time = "2025-09-09T15:57:36.255Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b1/dc226b4c90eb9f07a3fff95c2f0db3268e2e54e5cce97c4ac91518aee71b/numpy-2.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da1a74b90e7483d6ce5244053399a614b1d6b7bc30a60d2f570e5071f8959d3e", size = 16701722, upload-time = "2025-09-09T15:57:38.622Z" }, - { url = "https://files.pythonhosted.org/packages/9d/9d/9d8d358f2eb5eced14dba99f110d83b5cd9a4460895230f3b396ad19a323/numpy-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2990adf06d1ecee3b3dcbb4977dfab6e9f09807598d647f04d385d29e7a3c3d3", size = 16132755, upload-time = "2025-09-09T15:57:41.16Z" }, - { url = "https://files.pythonhosted.org/packages/b6/27/b3922660c45513f9377b3fb42240bec63f203c71416093476ec9aa0719dc/numpy-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ed635ff692483b8e3f0fcaa8e7eb8a75ee71aa6d975388224f70821421800cea", size = 18651560, upload-time = "2025-09-09T15:57:43.459Z" }, - { url = "https://files.pythonhosted.org/packages/5b/8e/3ab61a730bdbbc201bb245a71102aa609f0008b9ed15255500a99cd7f780/numpy-2.3.3-cp313-cp313t-win32.whl", hash = "sha256:a333b4ed33d8dc2b373cc955ca57babc00cd6f9009991d9edc5ddbc1bac36bcd", size = 6442776, upload-time = "2025-09-09T15:57:45.793Z" }, - { url = "https://files.pythonhosted.org/packages/1c/3a/e22b766b11f6030dc2decdeff5c2fb1610768055603f9f3be88b6d192fb2/numpy-2.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4384a169c4d8f97195980815d6fcad04933a7e1ab3b530921c3fef7a1c63426d", size = 12927281, upload-time = "2025-09-09T15:57:47.492Z" }, - { url = "https://files.pythonhosted.org/packages/7b/42/c2e2bc48c5e9b2a83423f99733950fbefd86f165b468a3d85d52b30bf782/numpy-2.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:75370986cc0bc66f4ce5110ad35aae6d182cc4ce6433c40ad151f53690130bf1", size = 10265275, upload-time = "2025-09-09T15:57:49.647Z" }, - { url = "https://files.pythonhosted.org/packages/6b/01/342ad585ad82419b99bcf7cebe99e61da6bedb89e213c5fd71acc467faee/numpy-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cd052f1fa6a78dee696b58a914b7229ecfa41f0a6d96dc663c1220a55e137593", size = 20951527, upload-time = "2025-09-09T15:57:52.006Z" }, - { url = "https://files.pythonhosted.org/packages/ef/d8/204e0d73fc1b7a9ee80ab1fe1983dd33a4d64a4e30a05364b0208e9a241a/numpy-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:414a97499480067d305fcac9716c29cf4d0d76db6ebf0bf3cbce666677f12652", size = 14186159, upload-time = "2025-09-09T15:57:54.407Z" }, - { url = "https://files.pythonhosted.org/packages/22/af/f11c916d08f3a18fb8ba81ab72b5b74a6e42ead4c2846d270eb19845bf74/numpy-2.3.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:50a5fe69f135f88a2be9b6ca0481a68a136f6febe1916e4920e12f1a34e708a7", size = 5114624, upload-time = "2025-09-09T15:57:56.5Z" }, - { url = "https://files.pythonhosted.org/packages/fb/11/0ed919c8381ac9d2ffacd63fd1f0c34d27e99cab650f0eb6f110e6ae4858/numpy-2.3.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:b912f2ed2b67a129e6a601e9d93d4fa37bef67e54cac442a2f588a54afe5c67a", size = 6642627, upload-time = "2025-09-09T15:57:58.206Z" }, - { url = "https://files.pythonhosted.org/packages/ee/83/deb5f77cb0f7ba6cb52b91ed388b47f8f3c2e9930d4665c600408d9b90b9/numpy-2.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e318ee0596d76d4cb3d78535dc005fa60e5ea348cd131a51e99d0bdbe0b54fe", size = 14296926, upload-time = "2025-09-09T15:58:00.035Z" }, - { url = "https://files.pythonhosted.org/packages/77/cc/70e59dcb84f2b005d4f306310ff0a892518cc0c8000a33d0e6faf7ca8d80/numpy-2.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce020080e4a52426202bdb6f7691c65bb55e49f261f31a8f506c9f6bc7450421", size = 16638958, upload-time = "2025-09-09T15:58:02.738Z" }, - { url = "https://files.pythonhosted.org/packages/b6/5a/b2ab6c18b4257e099587d5b7f903317bd7115333ad8d4ec4874278eafa61/numpy-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6687dc183aa55dae4a705b35f9c0f8cb178bcaa2f029b241ac5356221d5c021", size = 16071920, upload-time = "2025-09-09T15:58:05.029Z" }, - { url = "https://files.pythonhosted.org/packages/b8/f1/8b3fdc44324a259298520dd82147ff648979bed085feeacc1250ef1656c0/numpy-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d8f3b1080782469fdc1718c4ed1d22549b5fb12af0d57d35e992158a772a37cf", size = 18577076, upload-time = "2025-09-09T15:58:07.745Z" }, - { url = "https://files.pythonhosted.org/packages/f0/a1/b87a284fb15a42e9274e7fcea0dad259d12ddbf07c1595b26883151ca3b4/numpy-2.3.3-cp314-cp314-win32.whl", hash = "sha256:cb248499b0bc3be66ebd6578b83e5acacf1d6cb2a77f2248ce0e40fbec5a76d0", size = 6366952, upload-time = "2025-09-09T15:58:10.096Z" }, - { url = "https://files.pythonhosted.org/packages/70/5f/1816f4d08f3b8f66576d8433a66f8fa35a5acfb3bbd0bf6c31183b003f3d/numpy-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:691808c2b26b0f002a032c73255d0bd89751425f379f7bcd22d140db593a96e8", size = 12919322, upload-time = "2025-09-09T15:58:12.138Z" }, - { url = "https://files.pythonhosted.org/packages/8c/de/072420342e46a8ea41c324a555fa90fcc11637583fb8df722936aed1736d/numpy-2.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:9ad12e976ca7b10f1774b03615a2a4bab8addce37ecc77394d8e986927dc0dfe", size = 10478630, upload-time = "2025-09-09T15:58:14.64Z" }, - { url = "https://files.pythonhosted.org/packages/d5/df/ee2f1c0a9de7347f14da5dd3cd3c3b034d1b8607ccb6883d7dd5c035d631/numpy-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9cc48e09feb11e1db00b320e9d30a4151f7369afb96bd0e48d942d09da3a0d00", size = 21047987, upload-time = "2025-09-09T15:58:16.889Z" }, - { url = "https://files.pythonhosted.org/packages/d6/92/9453bdc5a4e9e69cf4358463f25e8260e2ffc126d52e10038b9077815989/numpy-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:901bf6123879b7f251d3631967fd574690734236075082078e0571977c6a8e6a", size = 14301076, upload-time = "2025-09-09T15:58:20.343Z" }, - { url = "https://files.pythonhosted.org/packages/13/77/1447b9eb500f028bb44253105bd67534af60499588a5149a94f18f2ca917/numpy-2.3.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:7f025652034199c301049296b59fa7d52c7e625017cae4c75d8662e377bf487d", size = 5229491, upload-time = "2025-09-09T15:58:22.481Z" }, - { url = "https://files.pythonhosted.org/packages/3d/f9/d72221b6ca205f9736cb4b2ce3b002f6e45cd67cd6a6d1c8af11a2f0b649/numpy-2.3.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:533ca5f6d325c80b6007d4d7fb1984c303553534191024ec6a524a4c92a5935a", size = 6737913, upload-time = "2025-09-09T15:58:24.569Z" }, - { url = "https://files.pythonhosted.org/packages/3c/5f/d12834711962ad9c46af72f79bb31e73e416ee49d17f4c797f72c96b6ca5/numpy-2.3.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0edd58682a399824633b66885d699d7de982800053acf20be1eaa46d92009c54", size = 14352811, upload-time = "2025-09-09T15:58:26.416Z" }, - { url = "https://files.pythonhosted.org/packages/a1/0d/fdbec6629d97fd1bebed56cd742884e4eead593611bbe1abc3eb40d304b2/numpy-2.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:367ad5d8fbec5d9296d18478804a530f1191e24ab4d75ab408346ae88045d25e", size = 16702689, upload-time = "2025-09-09T15:58:28.831Z" }, - { url = "https://files.pythonhosted.org/packages/9b/09/0a35196dc5575adde1eb97ddfbc3e1687a814f905377621d18ca9bc2b7dd/numpy-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8f6ac61a217437946a1fa48d24c47c91a0c4f725237871117dea264982128097", size = 16133855, upload-time = "2025-09-09T15:58:31.349Z" }, - { url = "https://files.pythonhosted.org/packages/7a/ca/c9de3ea397d576f1b6753eaa906d4cdef1bf97589a6d9825a349b4729cc2/numpy-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:179a42101b845a816d464b6fe9a845dfaf308fdfc7925387195570789bb2c970", size = 18652520, upload-time = "2025-09-09T15:58:33.762Z" }, - { url = "https://files.pythonhosted.org/packages/fd/c2/e5ed830e08cd0196351db55db82f65bc0ab05da6ef2b72a836dcf1936d2f/numpy-2.3.3-cp314-cp314t-win32.whl", hash = "sha256:1250c5d3d2562ec4174bce2e3a1523041595f9b651065e4a4473f5f48a6bc8a5", size = 6515371, upload-time = "2025-09-09T15:58:36.04Z" }, - { url = "https://files.pythonhosted.org/packages/47/c7/b0f6b5b67f6788a0725f744496badbb604d226bf233ba716683ebb47b570/numpy-2.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b37a0b2e5935409daebe82c1e42274d30d9dd355852529eab91dab8dcca7419f", size = 13112576, upload-time = "2025-09-09T15:58:37.927Z" }, - { url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b", size = 10545953, upload-time = "2025-09-09T15:58:40.576Z" }, +version = "2.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/f4/098d2270d52b41f1bd7db9fc288aaa0400cb48c2a3e2af6fa365d9720947/numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a", size = 20582187, upload-time = "2025-10-15T16:18:11.77Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/7e/b72610cc91edf138bc588df5150957a4937221ca6058b825b4725c27be62/numpy-2.3.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c090d4860032b857d94144d1a9976b8e36709e40386db289aaf6672de2a81966", size = 20950335, upload-time = "2025-10-15T16:16:10.304Z" }, + { url = "https://files.pythonhosted.org/packages/3e/46/bdd3370dcea2f95ef14af79dbf81e6927102ddf1cc54adc0024d61252fd9/numpy-2.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a13fc473b6db0be619e45f11f9e81260f7302f8d180c49a22b6e6120022596b3", size = 14179878, upload-time = "2025-10-15T16:16:12.595Z" }, + { url = "https://files.pythonhosted.org/packages/ac/01/5a67cb785bda60f45415d09c2bc245433f1c68dd82eef9c9002c508b5a65/numpy-2.3.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:3634093d0b428e6c32c3a69b78e554f0cd20ee420dcad5a9f3b2a63762ce4197", size = 5108673, upload-time = "2025-10-15T16:16:14.877Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cd/8428e23a9fcebd33988f4cb61208fda832800ca03781f471f3727a820704/numpy-2.3.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:043885b4f7e6e232d7df4f51ffdef8c36320ee9d5f227b380ea636722c7ed12e", size = 6641438, upload-time = "2025-10-15T16:16:16.805Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d1/913fe563820f3c6b079f992458f7331278dcd7ba8427e8e745af37ddb44f/numpy-2.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7", size = 14281290, upload-time = "2025-10-15T16:16:18.764Z" }, + { url = "https://files.pythonhosted.org/packages/9e/7e/7d306ff7cb143e6d975cfa7eb98a93e73495c4deabb7d1b5ecf09ea0fd69/numpy-2.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc8a63918b04b8571789688b2780ab2b4a33ab44bfe8ccea36d3eba51228c953", size = 16636543, upload-time = "2025-10-15T16:16:21.072Z" }, + { url = "https://files.pythonhosted.org/packages/47/6a/8cfc486237e56ccfb0db234945552a557ca266f022d281a2f577b98e955c/numpy-2.3.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40cc556d5abbc54aabe2b1ae287042d7bdb80c08edede19f0c0afb36ae586f37", size = 16056117, upload-time = "2025-10-15T16:16:23.369Z" }, + { url = "https://files.pythonhosted.org/packages/b1/0e/42cb5e69ea901e06ce24bfcc4b5664a56f950a70efdcf221f30d9615f3f3/numpy-2.3.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ecb63014bb7f4ce653f8be7f1df8cbc6093a5a2811211770f6606cc92b5a78fd", size = 18577788, upload-time = "2025-10-15T16:16:27.496Z" }, + { url = "https://files.pythonhosted.org/packages/86/92/41c3d5157d3177559ef0a35da50f0cda7fa071f4ba2306dd36818591a5bc/numpy-2.3.4-cp313-cp313-win32.whl", hash = "sha256:e8370eb6925bb8c1c4264fec52b0384b44f675f191df91cbe0140ec9f0955646", size = 6282620, upload-time = "2025-10-15T16:16:29.811Z" }, + { url = "https://files.pythonhosted.org/packages/09/97/fd421e8bc50766665ad35536c2bb4ef916533ba1fdd053a62d96cc7c8b95/numpy-2.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:56209416e81a7893036eea03abcb91c130643eb14233b2515c90dcac963fe99d", size = 12784672, upload-time = "2025-10-15T16:16:31.589Z" }, + { url = "https://files.pythonhosted.org/packages/ad/df/5474fb2f74970ca8eb978093969b125a84cc3d30e47f82191f981f13a8a0/numpy-2.3.4-cp313-cp313-win_arm64.whl", hash = "sha256:a700a4031bc0fd6936e78a752eefb79092cecad2599ea9c8039c548bc097f9bc", size = 10196702, upload-time = "2025-10-15T16:16:33.902Z" }, + { url = "https://files.pythonhosted.org/packages/11/83/66ac031464ec1767ea3ed48ce40f615eb441072945e98693bec0bcd056cc/numpy-2.3.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:86966db35c4040fdca64f0816a1c1dd8dbd027d90fca5a57e00e1ca4cd41b879", size = 21049003, upload-time = "2025-10-15T16:16:36.101Z" }, + { url = "https://files.pythonhosted.org/packages/5f/99/5b14e0e686e61371659a1d5bebd04596b1d72227ce36eed121bb0aeab798/numpy-2.3.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:838f045478638b26c375ee96ea89464d38428c69170360b23a1a50fa4baa3562", size = 14302980, upload-time = "2025-10-15T16:16:39.124Z" }, + { url = "https://files.pythonhosted.org/packages/2c/44/e9486649cd087d9fc6920e3fc3ac2aba10838d10804b1e179fb7cbc4e634/numpy-2.3.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d7315ed1dab0286adca467377c8381cd748f3dc92235f22a7dfc42745644a96a", size = 5231472, upload-time = "2025-10-15T16:16:41.168Z" }, + { url = "https://files.pythonhosted.org/packages/3e/51/902b24fa8887e5fe2063fd61b1895a476d0bbf46811ab0c7fdf4bd127345/numpy-2.3.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:84f01a4d18b2cc4ade1814a08e5f3c907b079c847051d720fad15ce37aa930b6", size = 6739342, upload-time = "2025-10-15T16:16:43.777Z" }, + { url = "https://files.pythonhosted.org/packages/34/f1/4de9586d05b1962acdcdb1dc4af6646361a643f8c864cef7c852bf509740/numpy-2.3.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:817e719a868f0dacde4abdfc5c1910b301877970195db9ab6a5e2c4bd5b121f7", size = 14354338, upload-time = "2025-10-15T16:16:46.081Z" }, + { url = "https://files.pythonhosted.org/packages/1f/06/1c16103b425de7969d5a76bdf5ada0804b476fed05d5f9e17b777f1cbefd/numpy-2.3.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85e071da78d92a214212cacea81c6da557cab307f2c34b5f85b628e94803f9c0", size = 16702392, upload-time = "2025-10-15T16:16:48.455Z" }, + { url = "https://files.pythonhosted.org/packages/34/b2/65f4dc1b89b5322093572b6e55161bb42e3e0487067af73627f795cc9d47/numpy-2.3.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2ec646892819370cf3558f518797f16597b4e4669894a2ba712caccc9da53f1f", size = 16134998, upload-time = "2025-10-15T16:16:51.114Z" }, + { url = "https://files.pythonhosted.org/packages/d4/11/94ec578896cdb973aaf56425d6c7f2aff4186a5c00fac15ff2ec46998b46/numpy-2.3.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:035796aaaddfe2f9664b9a9372f089cfc88bd795a67bd1bfe15e6e770934cf64", size = 18651574, upload-time = "2025-10-15T16:16:53.429Z" }, + { url = "https://files.pythonhosted.org/packages/62/b7/7efa763ab33dbccf56dade36938a77345ce8e8192d6b39e470ca25ff3cd0/numpy-2.3.4-cp313-cp313t-win32.whl", hash = "sha256:fea80f4f4cf83b54c3a051f2f727870ee51e22f0248d3114b8e755d160b38cfb", size = 6413135, upload-time = "2025-10-15T16:16:55.992Z" }, + { url = "https://files.pythonhosted.org/packages/43/70/aba4c38e8400abcc2f345e13d972fb36c26409b3e644366db7649015f291/numpy-2.3.4-cp313-cp313t-win_amd64.whl", hash = "sha256:15eea9f306b98e0be91eb344a94c0e630689ef302e10c2ce5f7e11905c704f9c", size = 12928582, upload-time = "2025-10-15T16:16:57.943Z" }, + { url = "https://files.pythonhosted.org/packages/67/63/871fad5f0073fc00fbbdd7232962ea1ac40eeaae2bba66c76214f7954236/numpy-2.3.4-cp313-cp313t-win_arm64.whl", hash = "sha256:b6c231c9c2fadbae4011ca5e7e83e12dc4a5072f1a1d85a0a7b3ed754d145a40", size = 10266691, upload-time = "2025-10-15T16:17:00.048Z" }, + { url = "https://files.pythonhosted.org/packages/72/71/ae6170143c115732470ae3a2d01512870dd16e0953f8a6dc89525696069b/numpy-2.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81c3e6d8c97295a7360d367f9f8553973651b76907988bb6066376bc2252f24e", size = 20955580, upload-time = "2025-10-15T16:17:02.509Z" }, + { url = "https://files.pythonhosted.org/packages/af/39/4be9222ffd6ca8a30eda033d5f753276a9c3426c397bb137d8e19dedd200/numpy-2.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7c26b0b2bf58009ed1f38a641f3db4be8d960a417ca96d14e5b06df1506d41ff", size = 14188056, upload-time = "2025-10-15T16:17:04.873Z" }, + { url = "https://files.pythonhosted.org/packages/6c/3d/d85f6700d0a4aa4f9491030e1021c2b2b7421b2b38d01acd16734a2bfdc7/numpy-2.3.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:62b2198c438058a20b6704351b35a1d7db881812d8512d67a69c9de1f18ca05f", size = 5116555, upload-time = "2025-10-15T16:17:07.499Z" }, + { url = "https://files.pythonhosted.org/packages/bf/04/82c1467d86f47eee8a19a464c92f90a9bb68ccf14a54c5224d7031241ffb/numpy-2.3.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:9d729d60f8d53a7361707f4b68a9663c968882dd4f09e0d58c044c8bf5faee7b", size = 6643581, upload-time = "2025-10-15T16:17:09.774Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d3/c79841741b837e293f48bd7db89d0ac7a4f2503b382b78a790ef1dc778a5/numpy-2.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd0c630cf256b0a7fd9d0a11c9413b42fef5101219ce6ed5a09624f5a65392c7", size = 14299186, upload-time = "2025-10-15T16:17:11.937Z" }, + { url = "https://files.pythonhosted.org/packages/e8/7e/4a14a769741fbf237eec5a12a2cbc7a4c4e061852b6533bcb9e9a796c908/numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5e081bc082825f8b139f9e9fe42942cb4054524598aaeb177ff476cc76d09d2", size = 16638601, upload-time = "2025-10-15T16:17:14.391Z" }, + { url = "https://files.pythonhosted.org/packages/93/87/1c1de269f002ff0a41173fe01dcc925f4ecff59264cd8f96cf3b60d12c9b/numpy-2.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15fb27364ed84114438fff8aaf998c9e19adbeba08c0b75409f8c452a8692c52", size = 16074219, upload-time = "2025-10-15T16:17:17.058Z" }, + { url = "https://files.pythonhosted.org/packages/cd/28/18f72ee77408e40a76d691001ae599e712ca2a47ddd2c4f695b16c65f077/numpy-2.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:85d9fb2d8cd998c84d13a79a09cc0c1091648e848e4e6249b0ccd7f6b487fa26", size = 18576702, upload-time = "2025-10-15T16:17:19.379Z" }, + { url = "https://files.pythonhosted.org/packages/c3/76/95650169b465ececa8cf4b2e8f6df255d4bf662775e797ade2025cc51ae6/numpy-2.3.4-cp314-cp314-win32.whl", hash = "sha256:e73d63fd04e3a9d6bc187f5455d81abfad05660b212c8804bf3b407e984cd2bc", size = 6337136, upload-time = "2025-10-15T16:17:22.886Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/a231a5c43ede5d6f77ba4a91e915a87dea4aeea76560ba4d2bf185c683f0/numpy-2.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:3da3491cee49cf16157e70f607c03a217ea6647b1cea4819c4f48e53d49139b9", size = 12920542, upload-time = "2025-10-15T16:17:24.783Z" }, + { url = "https://files.pythonhosted.org/packages/0d/0c/ae9434a888f717c5ed2ff2393b3f344f0ff6f1c793519fa0c540461dc530/numpy-2.3.4-cp314-cp314-win_arm64.whl", hash = "sha256:6d9cd732068e8288dbe2717177320723ccec4fb064123f0caf9bbd90ab5be868", size = 10480213, upload-time = "2025-10-15T16:17:26.935Z" }, + { url = "https://files.pythonhosted.org/packages/83/4b/c4a5f0841f92536f6b9592694a5b5f68c9ab37b775ff342649eadf9055d3/numpy-2.3.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:22758999b256b595cf0b1d102b133bb61866ba5ceecf15f759623b64c020c9ec", size = 21052280, upload-time = "2025-10-15T16:17:29.638Z" }, + { url = "https://files.pythonhosted.org/packages/3e/80/90308845fc93b984d2cc96d83e2324ce8ad1fd6efea81b324cba4b673854/numpy-2.3.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9cb177bc55b010b19798dc5497d540dea67fd13a8d9e882b2dae71de0cf09eb3", size = 14302930, upload-time = "2025-10-15T16:17:32.384Z" }, + { url = "https://files.pythonhosted.org/packages/3d/4e/07439f22f2a3b247cec4d63a713faae55e1141a36e77fb212881f7cda3fb/numpy-2.3.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0f2bcc76f1e05e5ab58893407c63d90b2029908fa41f9f1cc51eecce936c3365", size = 5231504, upload-time = "2025-10-15T16:17:34.515Z" }, + { url = "https://files.pythonhosted.org/packages/ab/de/1e11f2547e2fe3d00482b19721855348b94ada8359aef5d40dd57bfae9df/numpy-2.3.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dc20bde86802df2ed8397a08d793da0ad7a5fd4ea3ac85d757bf5dd4ad7c252", size = 6739405, upload-time = "2025-10-15T16:17:36.128Z" }, + { url = "https://files.pythonhosted.org/packages/3b/40/8cd57393a26cebe2e923005db5134a946c62fa56a1087dc7c478f3e30837/numpy-2.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e199c087e2aa71c8f9ce1cb7a8e10677dc12457e7cc1be4798632da37c3e86e", size = 14354866, upload-time = "2025-10-15T16:17:38.884Z" }, + { url = "https://files.pythonhosted.org/packages/93/39/5b3510f023f96874ee6fea2e40dfa99313a00bf3ab779f3c92978f34aace/numpy-2.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85597b2d25ddf655495e2363fe044b0ae999b75bc4d630dc0d886484b03a5eb0", size = 16703296, upload-time = "2025-10-15T16:17:41.564Z" }, + { url = "https://files.pythonhosted.org/packages/41/0d/19bb163617c8045209c1996c4e427bccbc4bbff1e2c711f39203c8ddbb4a/numpy-2.3.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04a69abe45b49c5955923cf2c407843d1c85013b424ae8a560bba16c92fe44a0", size = 16136046, upload-time = "2025-10-15T16:17:43.901Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c1/6dba12fdf68b02a21ac411c9df19afa66bed2540f467150ca64d246b463d/numpy-2.3.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e1708fac43ef8b419c975926ce1eaf793b0c13b7356cfab6ab0dc34c0a02ac0f", size = 18652691, upload-time = "2025-10-15T16:17:46.247Z" }, + { url = "https://files.pythonhosted.org/packages/f8/73/f85056701dbbbb910c51d846c58d29fd46b30eecd2b6ba760fc8b8a1641b/numpy-2.3.4-cp314-cp314t-win32.whl", hash = "sha256:863e3b5f4d9915aaf1b8ec79ae560ad21f0b8d5e3adc31e73126491bb86dee1d", size = 6485782, upload-time = "2025-10-15T16:17:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/17/90/28fa6f9865181cb817c2471ee65678afa8a7e2a1fb16141473d5fa6bacc3/numpy-2.3.4-cp314-cp314t-win_amd64.whl", hash = "sha256:962064de37b9aef801d33bc579690f8bfe6c5e70e29b61783f60bcba838a14d6", size = 13113301, upload-time = "2025-10-15T16:17:50.938Z" }, + { url = "https://files.pythonhosted.org/packages/54/23/08c002201a8e7e1f9afba93b97deceb813252d9cfd0d3351caed123dcf97/numpy-2.3.4-cp314-cp314t-win_arm64.whl", hash = "sha256:8b5a9a39c45d852b62693d9b3f3e0fe052541f804296ff401a72a1b60edafb29", size = 10547532, upload-time = "2025-10-15T16:17:53.48Z" }, ] [[package]] @@ -1172,57 +1172,60 @@ wheels = [ [[package]] name = "pillow" -version = "11.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" }, - { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" }, - { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" }, - { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" }, - { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" }, - { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" }, - { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" }, - { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" }, - { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" }, - { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" }, - { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" }, - { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" }, - { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" }, - { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" }, - { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" }, - { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" }, - { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" }, - { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" }, - { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" }, - { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" }, - { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" }, - { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" }, - { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" }, - { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" }, - { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" }, - { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" }, - { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" }, - { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" }, - { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" }, - { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" }, - { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" }, - { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" }, - { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" }, - { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" }, +version = "12.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828, upload-time = "2025-10-15T18:24:14.008Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/f2/de993bb2d21b33a98d031ecf6a978e4b61da207bef02f7b43093774c480d/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", size = 4045493, upload-time = "2025-10-15T18:22:25.758Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b6/bc8d0c4c9f6f111a783d045310945deb769b806d7574764234ffd50bc5ea/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", size = 4120461, upload-time = "2025-10-15T18:22:27.286Z" }, + { url = "https://files.pythonhosted.org/packages/5d/57/d60d343709366a353dc56adb4ee1e7d8a2cc34e3fbc22905f4167cfec119/pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", size = 3576912, upload-time = "2025-10-15T18:22:28.751Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a4/a0a31467e3f83b94d37568294b01d22b43ae3c5d85f2811769b9c66389dd/pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", size = 5249132, upload-time = "2025-10-15T18:22:30.641Z" }, + { url = "https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", size = 4650099, upload-time = "2025-10-15T18:22:32.73Z" }, + { url = "https://files.pythonhosted.org/packages/fc/bd/69ed99fd46a8dba7c1887156d3572fe4484e3f031405fcc5a92e31c04035/pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", size = 6230808, upload-time = "2025-10-15T18:22:34.337Z" }, + { url = "https://files.pythonhosted.org/packages/ea/94/8fad659bcdbf86ed70099cb60ae40be6acca434bbc8c4c0d4ef356d7e0de/pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", size = 8037804, upload-time = "2025-10-15T18:22:36.402Z" }, + { url = "https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", size = 6345553, upload-time = "2025-10-15T18:22:38.066Z" }, + { url = "https://files.pythonhosted.org/packages/38/57/755dbd06530a27a5ed74f8cb0a7a44a21722ebf318edbe67ddbd7fb28f88/pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", size = 7037729, upload-time = "2025-10-15T18:22:39.769Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/7e94f4c41d238615674d06ed677c14883103dce1c52e4af16f000338cfd7/pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", size = 6459789, upload-time = "2025-10-15T18:22:41.437Z" }, + { url = "https://files.pythonhosted.org/packages/9c/14/4448bb0b5e0f22dd865290536d20ec8a23b64e2d04280b89139f09a36bb6/pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", size = 7130917, upload-time = "2025-10-15T18:22:43.152Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ca/16c6926cc1c015845745d5c16c9358e24282f1e588237a4c36d2b30f182f/pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", size = 6302391, upload-time = "2025-10-15T18:22:44.753Z" }, + { url = "https://files.pythonhosted.org/packages/6d/2a/dd43dcfd6dae9b6a49ee28a8eedb98c7d5ff2de94a5d834565164667b97b/pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", size = 7007477, upload-time = "2025-10-15T18:22:46.838Z" }, + { url = "https://files.pythonhosted.org/packages/77/f0/72ea067f4b5ae5ead653053212af05ce3705807906ba3f3e8f58ddf617e6/pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", size = 2435918, upload-time = "2025-10-15T18:22:48.399Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5e/9046b423735c21f0487ea6cb5b10f89ea8f8dfbe32576fe052b5ba9d4e5b/pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", size = 5251406, upload-time = "2025-10-15T18:22:49.905Z" }, + { url = "https://files.pythonhosted.org/packages/12/66/982ceebcdb13c97270ef7a56c3969635b4ee7cd45227fa707c94719229c5/pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", size = 4653218, upload-time = "2025-10-15T18:22:51.587Z" }, + { url = "https://files.pythonhosted.org/packages/16/b3/81e625524688c31859450119bf12674619429cab3119eec0e30a7a1029cb/pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", size = 6266564, upload-time = "2025-10-15T18:22:53.215Z" }, + { url = "https://files.pythonhosted.org/packages/98/59/dfb38f2a41240d2408096e1a76c671d0a105a4a8471b1871c6902719450c/pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", size = 8069260, upload-time = "2025-10-15T18:22:54.933Z" }, + { url = "https://files.pythonhosted.org/packages/dc/3d/378dbea5cd1874b94c312425ca77b0f47776c78e0df2df751b820c8c1d6c/pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", size = 6379248, upload-time = "2025-10-15T18:22:56.605Z" }, + { url = "https://files.pythonhosted.org/packages/84/b0/d525ef47d71590f1621510327acec75ae58c721dc071b17d8d652ca494d8/pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", size = 7066043, upload-time = "2025-10-15T18:22:58.53Z" }, + { url = "https://files.pythonhosted.org/packages/61/2c/aced60e9cf9d0cde341d54bf7932c9ffc33ddb4a1595798b3a5150c7ec4e/pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", size = 6490915, upload-time = "2025-10-15T18:23:00.582Z" }, + { url = "https://files.pythonhosted.org/packages/ef/26/69dcb9b91f4e59f8f34b2332a4a0a951b44f547c4ed39d3e4dcfcff48f89/pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", size = 7157998, upload-time = "2025-10-15T18:23:02.627Z" }, + { url = "https://files.pythonhosted.org/packages/61/2b/726235842220ca95fa441ddf55dd2382b52ab5b8d9c0596fe6b3f23dafe8/pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", size = 6306201, upload-time = "2025-10-15T18:23:04.709Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3d/2afaf4e840b2df71344ababf2f8edd75a705ce500e5dc1e7227808312ae1/pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", size = 7013165, upload-time = "2025-10-15T18:23:06.46Z" }, + { url = "https://files.pythonhosted.org/packages/6f/75/3fa09aa5cf6ed04bee3fa575798ddf1ce0bace8edb47249c798077a81f7f/pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", size = 2437834, upload-time = "2025-10-15T18:23:08.194Z" }, + { url = "https://files.pythonhosted.org/packages/54/2a/9a8c6ba2c2c07b71bec92cf63e03370ca5e5f5c5b119b742bcc0cde3f9c5/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", size = 4045531, upload-time = "2025-10-15T18:23:10.121Z" }, + { url = "https://files.pythonhosted.org/packages/84/54/836fdbf1bfb3d66a59f0189ff0b9f5f666cee09c6188309300df04ad71fa/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", size = 4120554, upload-time = "2025-10-15T18:23:12.14Z" }, + { url = "https://files.pythonhosted.org/packages/0d/cd/16aec9f0da4793e98e6b54778a5fbce4f375c6646fe662e80600b8797379/pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", size = 3576812, upload-time = "2025-10-15T18:23:13.962Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b7/13957fda356dc46339298b351cae0d327704986337c3c69bb54628c88155/pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", size = 5252689, upload-time = "2025-10-15T18:23:15.562Z" }, + { url = "https://files.pythonhosted.org/packages/fc/f5/eae31a306341d8f331f43edb2e9122c7661b975433de5e447939ae61c5da/pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", size = 4650186, upload-time = "2025-10-15T18:23:17.379Z" }, + { url = "https://files.pythonhosted.org/packages/86/62/2a88339aa40c4c77e79108facbd307d6091e2c0eb5b8d3cf4977cfca2fe6/pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", size = 6230308, upload-time = "2025-10-15T18:23:18.971Z" }, + { url = "https://files.pythonhosted.org/packages/c7/33/5425a8992bcb32d1cb9fa3dd39a89e613d09a22f2c8083b7bf43c455f760/pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", size = 8039222, upload-time = "2025-10-15T18:23:20.909Z" }, + { url = "https://files.pythonhosted.org/packages/d8/61/3f5d3b35c5728f37953d3eec5b5f3e77111949523bd2dd7f31a851e50690/pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", size = 6346657, upload-time = "2025-10-15T18:23:23.077Z" }, + { url = "https://files.pythonhosted.org/packages/3a/be/ee90a3d79271227e0f0a33c453531efd6ed14b2e708596ba5dd9be948da3/pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", size = 7038482, upload-time = "2025-10-15T18:23:25.005Z" }, + { url = "https://files.pythonhosted.org/packages/44/34/a16b6a4d1ad727de390e9bd9f19f5f669e079e5826ec0f329010ddea492f/pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", size = 6461416, upload-time = "2025-10-15T18:23:27.009Z" }, + { url = "https://files.pythonhosted.org/packages/b6/39/1aa5850d2ade7d7ba9f54e4e4c17077244ff7a2d9e25998c38a29749eb3f/pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", size = 7131584, upload-time = "2025-10-15T18:23:29.752Z" }, + { url = "https://files.pythonhosted.org/packages/bf/db/4fae862f8fad0167073a7733973bfa955f47e2cac3dc3e3e6257d10fab4a/pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", size = 6400621, upload-time = "2025-10-15T18:23:32.06Z" }, + { url = "https://files.pythonhosted.org/packages/2b/24/b350c31543fb0107ab2599464d7e28e6f856027aadda995022e695313d94/pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", size = 7142916, upload-time = "2025-10-15T18:23:34.71Z" }, + { url = "https://files.pythonhosted.org/packages/0f/9b/0ba5a6fd9351793996ef7487c4fdbde8d3f5f75dbedc093bb598648fddf0/pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", size = 2523836, upload-time = "2025-10-15T18:23:36.967Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7a/ceee0840aebc579af529b523d530840338ecf63992395842e54edc805987/pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", size = 5255092, upload-time = "2025-10-15T18:23:38.573Z" }, + { url = "https://files.pythonhosted.org/packages/44/76/20776057b4bfd1aef4eeca992ebde0f53a4dce874f3ae693d0ec90a4f79b/pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", size = 4653158, upload-time = "2025-10-15T18:23:40.238Z" }, + { url = "https://files.pythonhosted.org/packages/82/3f/d9ff92ace07be8836b4e7e87e6a4c7a8318d47c2f1463ffcf121fc57d9cb/pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1", size = 6267882, upload-time = "2025-10-15T18:23:42.434Z" }, + { url = "https://files.pythonhosted.org/packages/9f/7a/4f7ff87f00d3ad33ba21af78bfcd2f032107710baf8280e3722ceec28cda/pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", size = 8071001, upload-time = "2025-10-15T18:23:44.29Z" }, + { url = "https://files.pythonhosted.org/packages/75/87/fcea108944a52dad8cca0715ae6247e271eb80459364a98518f1e4f480c1/pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", size = 6380146, upload-time = "2025-10-15T18:23:46.065Z" }, + { url = "https://files.pythonhosted.org/packages/91/52/0d31b5e571ef5fd111d2978b84603fce26aba1b6092f28e941cb46570745/pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", size = 7067344, upload-time = "2025-10-15T18:23:47.898Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f4/2dd3d721f875f928d48e83bb30a434dee75a2531bca839bb996bb0aa5a91/pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", size = 6491864, upload-time = "2025-10-15T18:23:49.607Z" }, + { url = "https://files.pythonhosted.org/packages/30/4b/667dfcf3d61fc309ba5a15b141845cece5915e39b99c1ceab0f34bf1d124/pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", size = 7158911, upload-time = "2025-10-15T18:23:51.351Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2f/16cabcc6426c32218ace36bf0d55955e813f2958afddbf1d391849fee9d1/pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", size = 6408045, upload-time = "2025-10-15T18:23:53.177Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/e29aa0c9c666cf787628d3f0dcf379f4791fba79f4936d02f8b37165bdf8/pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", size = 7148282, upload-time = "2025-10-15T18:23:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630, upload-time = "2025-10-15T18:23:57.149Z" }, ] [[package]] @@ -1248,28 +1251,29 @@ wheels = [ [[package]] name = "protobuf" -version = "6.32.1" +version = "6.33.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/a4/cc17347aa2897568beece2e674674359f911d6fe21b0b8d6268cd42727ac/protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d", size = 440635, upload-time = "2025-09-11T21:38:42.935Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/ff/64a6c8f420818bb873713988ca5492cba3a7946be57e027ac63495157d97/protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954", size = 443463, upload-time = "2025-10-15T20:39:52.159Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/98/645183ea03ab3995d29086b8bf4f7562ebd3d10c9a4b14ee3f20d47cfe50/protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085", size = 424411, upload-time = "2025-09-11T21:38:27.427Z" }, - { url = "https://files.pythonhosted.org/packages/8c/f3/6f58f841f6ebafe076cebeae33fc336e900619d34b1c93e4b5c97a81fdfa/protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1", size = 435738, upload-time = "2025-09-11T21:38:30.959Z" }, - { url = "https://files.pythonhosted.org/packages/10/56/a8a3f4e7190837139e68c7002ec749190a163af3e330f65d90309145a210/protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281", size = 426454, upload-time = "2025-09-11T21:38:34.076Z" }, - { url = "https://files.pythonhosted.org/packages/3f/be/8dd0a927c559b37d7a6c8ab79034fd167dcc1f851595f2e641ad62be8643/protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4", size = 322874, upload-time = "2025-09-11T21:38:35.509Z" }, - { url = "https://files.pythonhosted.org/packages/5c/f6/88d77011b605ef979aace37b7703e4eefad066f7e84d935e5a696515c2dd/protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710", size = 322013, upload-time = "2025-09-11T21:38:37.017Z" }, - { url = "https://files.pythonhosted.org/packages/97/b7/15cc7d93443d6c6a84626ae3258a91f4c6ac8c0edd5df35ea7658f71b79c/protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346", size = 169289, upload-time = "2025-09-11T21:38:41.234Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ee/52b3fa8feb6db4a833dfea4943e175ce645144532e8a90f72571ad85df4e/protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035", size = 425593, upload-time = "2025-10-15T20:39:40.29Z" }, + { url = "https://files.pythonhosted.org/packages/7b/c6/7a465f1825872c55e0341ff4a80198743f73b69ce5d43ab18043699d1d81/protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee", size = 436882, upload-time = "2025-10-15T20:39:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/e1/a9/b6eee662a6951b9c3640e8e452ab3e09f117d99fc10baa32d1581a0d4099/protobuf-6.33.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455", size = 427521, upload-time = "2025-10-15T20:39:43.803Z" }, + { url = "https://files.pythonhosted.org/packages/10/35/16d31e0f92c6d2f0e77c2a3ba93185130ea13053dd16200a57434c882f2b/protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90", size = 324445, upload-time = "2025-10-15T20:39:44.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/eb/2a981a13e35cda8b75b5585aaffae2eb904f8f351bdd3870769692acbd8a/protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298", size = 339159, upload-time = "2025-10-15T20:39:46.186Z" }, + { url = "https://files.pythonhosted.org/packages/21/51/0b1cbad62074439b867b4e04cc09b93f6699d78fd191bed2bbb44562e077/protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef", size = 323172, upload-time = "2025-10-15T20:39:47.465Z" }, + { url = "https://files.pythonhosted.org/packages/07/d1/0a28c21707807c6aacd5dc9c3704b2aa1effbf37adebd8caeaf68b17a636/protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995", size = 170477, upload-time = "2025-10-15T20:39:51.311Z" }, ] [[package]] name = "psycopg" -version = "3.2.10" +version = "3.2.11" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/f1/0258a123c045afaf3c3b60c22ccff077bceeb24b8dc2c593270899353bd0/psycopg-3.2.10.tar.gz", hash = "sha256:0bce99269d16ed18401683a8569b2c5abd94f72f8364856d56c0389bcd50972a", size = 160380, upload-time = "2025-09-08T09:13:37.775Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/02/9fdfc018c026df2bcf9c11480c1014f9b90c6d801e5f929408cbfbf94cc0/psycopg-3.2.11.tar.gz", hash = "sha256:398bb484ed44361e041c8f804ed7af3d2fcefbffdace1d905b7446c319321706", size = 160644, upload-time = "2025-10-18T22:48:28.136Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/90/422ffbbeeb9418c795dae2a768db860401446af0c6768bc061ce22325f58/psycopg-3.2.10-py3-none-any.whl", hash = "sha256:ab5caf09a9ec42e314a21f5216dbcceac528e0e05142e42eea83a3b28b320ac3", size = 206586, upload-time = "2025-09-08T09:07:50.121Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1b/96ee90ed0007d64936d9bd1bb3108d0af3cf762b4f11dbd73359f0687c3d/psycopg-3.2.11-py3-none-any.whl", hash = "sha256:217231b2b6b72fba88281b94241b2f16043ee67f81def47c52a01b72ff0c086a", size = 206766, upload-time = "2025-10-18T22:43:32.114Z" }, ] [package.optional-dependencies] @@ -1279,27 +1283,27 @@ binary = [ [[package]] name = "psycopg-binary" -version = "3.2.10" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/80/db840f7ebf948ab05b4793ad34d4da6ad251829d6c02714445ae8b5f1403/psycopg_binary-3.2.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:55b14f2402be027fe1568bc6c4d75ac34628ff5442a70f74137dadf99f738e3b", size = 3982057, upload-time = "2025-09-08T09:10:28.725Z" }, - { url = "https://files.pythonhosted.org/packages/2d/53/39308328bb8388b1ec3501a16128c5ada405f217c6d91b3d921b9f3c5604/psycopg_binary-3.2.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:43d803fb4e108a67c78ba58f3e6855437ca25d56504cae7ebbfbd8fce9b59247", size = 4066830, upload-time = "2025-09-08T09:10:34.083Z" }, - { url = "https://files.pythonhosted.org/packages/e7/5a/18e6f41b40c71197479468cb18703b2999c6e4ab06f9c05df3bf416a55d7/psycopg_binary-3.2.10-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:470594d303928ab72a1ffd179c9c7bde9d00f76711d6b0c28f8a46ddf56d9807", size = 4610747, upload-time = "2025-09-08T09:10:39.697Z" }, - { url = "https://files.pythonhosted.org/packages/be/ab/9198fed279aca238c245553ec16504179d21aad049958a2865d0aa797db4/psycopg_binary-3.2.10-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a1d4e4d309049e3cb61269652a3ca56cb598da30ecd7eb8cea561e0d18bc1a43", size = 4700301, upload-time = "2025-09-08T09:10:44.715Z" }, - { url = "https://files.pythonhosted.org/packages/fc/0d/59024313b5e6c5da3e2a016103494c609d73a95157a86317e0f600c8acb3/psycopg_binary-3.2.10-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a92ff1c2cd79b3966d6a87e26ceb222ecd5581b5ae4b58961f126af806a861ed", size = 4392679, upload-time = "2025-09-08T09:10:49.106Z" }, - { url = "https://files.pythonhosted.org/packages/ff/47/21ef15d8a66e3a7a76a177f885173d27f0c5cbe39f5dd6eda9832d6b4e19/psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac0365398947879c9827b319217096be727da16c94422e0eb3cf98c930643162", size = 3857881, upload-time = "2025-09-08T09:10:56.75Z" }, - { url = "https://files.pythonhosted.org/packages/af/35/c5e5402ccd40016f15d708bbf343b8cf107a58f8ae34d14dc178fdea4fd4/psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:42ee399c2613b470a87084ed79b06d9d277f19b0457c10e03a4aef7059097abc", size = 3531135, upload-time = "2025-09-08T09:11:03.346Z" }, - { url = "https://files.pythonhosted.org/packages/e6/e2/9b82946859001fe5e546c8749991b8b3b283f40d51bdc897d7a8e13e0a5e/psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2028073fc12cd70ba003309d1439c0c4afab4a7eee7653b8c91213064fffe12b", size = 3581813, upload-time = "2025-09-08T09:11:08.76Z" }, - { url = "https://files.pythonhosted.org/packages/c5/91/c10cfccb75464adb4781486e0014ecd7c2ad6decf6cbe0afd8db65ac2bc9/psycopg_binary-3.2.10-cp313-cp313-win_amd64.whl", hash = "sha256:8390db6d2010ffcaf7f2b42339a2da620a7125d37029c1f9b72dfb04a8e7be6f", size = 2881466, upload-time = "2025-09-08T09:11:14.078Z" }, - { url = "https://files.pythonhosted.org/packages/fd/89/b0702ba0d007cc787dd7a205212c8c8cae229d1e7214c8e27bdd3b13d33e/psycopg_binary-3.2.10-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b34c278a58aa79562afe7f45e0455b1f4cad5974fc3d5674cc5f1f9f57e97fc5", size = 3981253, upload-time = "2025-09-08T09:11:19.864Z" }, - { url = "https://files.pythonhosted.org/packages/dc/c9/e51ac72ac34d1d8ea7fd861008ad8de60e56997f5bd3fbae7536570f6f58/psycopg_binary-3.2.10-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:810f65b9ef1fe9dddb5c05937884ea9563aaf4e1a2c3d138205231ed5f439511", size = 4067542, upload-time = "2025-09-08T09:11:25.366Z" }, - { url = "https://files.pythonhosted.org/packages/d6/27/49625c79ae89959a070c1fb63ebb5c6eed426fa09e15086b6f5b626fcdc2/psycopg_binary-3.2.10-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8923487c3898c65e1450847e15d734bb2e6adbd2e79d2d1dd5ad829a1306bdc0", size = 4615338, upload-time = "2025-09-08T09:11:31.079Z" }, - { url = "https://files.pythonhosted.org/packages/b9/0d/9fdb5482f50f56303770ea8a3b1c1f32105762da731c7e2a4f425e0b3887/psycopg_binary-3.2.10-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7950ff79df7a453ac8a7d7a74694055b6c15905b0a2b6e3c99eb59c51a3f9bf7", size = 4703401, upload-time = "2025-09-08T09:11:38.718Z" }, - { url = "https://files.pythonhosted.org/packages/3c/f3/eb2f75ca2c090bf1d0c90d6da29ef340876fe4533bcfc072a9fd94dd52b4/psycopg_binary-3.2.10-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c2b95e83fda70ed2b0b4fadd8538572e4a4d987b721823981862d1ab56cc760", size = 4393458, upload-time = "2025-09-08T09:11:44.114Z" }, - { url = "https://files.pythonhosted.org/packages/20/2e/887abe0591b2f1c1af31164b9efb46c5763e4418f403503bc9fbddaa02ef/psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20384985fbc650c09a547a13c6d7f91bb42020d38ceafd2b68b7fc4a48a1f160", size = 3863733, upload-time = "2025-09-08T09:11:49.237Z" }, - { url = "https://files.pythonhosted.org/packages/6b/8c/9446e3a84187220a98657ef778518f9b44eba55b1f6c3e8300d229ec9930/psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1f6982609b8ff8fcd67299b67cd5787da1876f3bb28fedd547262cfa8ddedf94", size = 3535121, upload-time = "2025-09-08T09:11:53.887Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e1/f0382c956bfaa951a0dbd4d5a354acf093ef7e5219996958143dfd2bf37d/psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bf30dcf6aaaa8d4779a20d2158bdf81cc8e84ce8eee595d748a7671c70c7b890", size = 3584235, upload-time = "2025-09-08T09:12:01.118Z" }, - { url = "https://files.pythonhosted.org/packages/5a/dd/464bd739bacb3b745a1c93bc15f20f0b1e27f0a64ec693367794b398673b/psycopg_binary-3.2.10-cp314-cp314-win_amd64.whl", hash = "sha256:d5c6a66a76022af41970bf19f51bc6bf87bd10165783dd1d40484bfd87d6b382", size = 2973554, upload-time = "2025-09-08T09:12:05.884Z" }, +version = "3.2.11" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/93/9cea78ed3b279909f0fd6c2badb24b2361b93c875d6a7c921e26f6254044/psycopg_binary-3.2.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47f6cf8a1d02d25238bdb8741ac641ff0ec22b1c6ff6a2acd057d0da5c712842", size = 4017939, upload-time = "2025-10-18T22:45:45.114Z" }, + { url = "https://files.pythonhosted.org/packages/58/86/fc9925f500b2c140c0bb8c1f8fcd04f8c45c76d4852e87baf4c75182de8c/psycopg_binary-3.2.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91268f04380964a5e767f8102d05f1e23312ddbe848de1a9514b08b3fc57d354", size = 4090150, upload-time = "2025-10-18T22:45:50.214Z" }, + { url = "https://files.pythonhosted.org/packages/4e/10/752b698da1ca9e6c5f15d8798cb637c3615315fd2da17eee4a90cf20ee08/psycopg_binary-3.2.11-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:199f88a05dd22133eab2deb30348ef7a70c23d706c8e63fdc904234163c63517", size = 4625597, upload-time = "2025-10-18T22:45:54.638Z" }, + { url = "https://files.pythonhosted.org/packages/0a/9f/b578545c3c23484f4e234282d97ab24632a1d3cbfec64209786872e7cc8f/psycopg_binary-3.2.11-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7b3c5474dbad63bcccb8d14d4d4c7c19f1dc6f8e8c1914cbc771d261cf8eddca", size = 4720326, upload-time = "2025-10-18T22:45:59.266Z" }, + { url = "https://files.pythonhosted.org/packages/43/3b/ba548d3fe65a7d4c96e568c2188e4b665802e3cba41664945ed95d16eae9/psycopg_binary-3.2.11-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:581358e770a4536e546841b78fd0fe318added4a82443bf22d0bbe3109cf9582", size = 4411647, upload-time = "2025-10-18T22:46:04.009Z" }, + { url = "https://files.pythonhosted.org/packages/26/65/559ab485b198600e7ff70d70786ae5c89d63475ca01d43a7dda0d7c91386/psycopg_binary-3.2.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:54a30f00a51b9043048b3e7ee806ffd31fc5fbd02a20f0e69d21306ff33dc473", size = 3863037, upload-time = "2025-10-18T22:46:08.469Z" }, + { url = "https://files.pythonhosted.org/packages/8c/29/05d0b48c8bef147e8216a36a1263a309a6240dcc09a56f5b8174fa6216d2/psycopg_binary-3.2.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2a438fad4cc081b018431fde0e791b6d50201526edf39522a85164f606c39ddb", size = 3536975, upload-time = "2025-10-18T22:46:12.982Z" }, + { url = "https://files.pythonhosted.org/packages/d4/75/304e133d3ab1a49602616192edb81f603ed574f79966449105f2e200999d/psycopg_binary-3.2.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f5e7415b5d0f58edf2708842c66605092df67f3821161d861b09695fc326c4de", size = 3586213, upload-time = "2025-10-18T22:46:19.523Z" }, + { url = "https://files.pythonhosted.org/packages/c0/10/c47cce42fa3c37d439e1400eaa5eeb2ce53dc3abc84d52c8a8a9e544d945/psycopg_binary-3.2.11-cp313-cp313-win_amd64.whl", hash = "sha256:6b9632c42f76d5349e7dd50025cff02688eb760b258e891ad2c6428e7e4917d5", size = 2912997, upload-time = "2025-10-18T22:46:24.978Z" }, + { url = "https://files.pythonhosted.org/packages/85/13/728b4763ef76a688737acebfcb5ab8696b024adc49a69c86081392b0e5ba/psycopg_binary-3.2.11-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:260738ae222b41dbefd0d84cb2e150a112f90b41688630f57fdac487ab6d6f38", size = 4016962, upload-time = "2025-10-18T22:46:29.207Z" }, + { url = "https://files.pythonhosted.org/packages/9f/0f/6180149621a907c5b60a2fae87d6ee10cc13e8c9f58d8250c310634ced04/psycopg_binary-3.2.11-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c594c199869099c59c85b9f4423370b6212491fb929e7fcda0da1768761a2c2c", size = 4090614, upload-time = "2025-10-18T22:46:33.073Z" }, + { url = "https://files.pythonhosted.org/packages/f8/97/cce19bdef510b698c9036d5573b941b539ffcaa7602450da559c8a62e0c3/psycopg_binary-3.2.11-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5768a9e7d393b2edd3a28de5a6d5850d054a016ed711f7044a9072f19f5e50d5", size = 4629749, upload-time = "2025-10-18T22:46:37.415Z" }, + { url = "https://files.pythonhosted.org/packages/93/9d/9bff18989fb2bf05d18c1431dd8bec4a1d90141beb11fc45d3269947ddf3/psycopg_binary-3.2.11-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:27eb6367350b75fef882c40cd6f748bfd976db2f8651f7511956f11efc15154f", size = 4724035, upload-time = "2025-10-18T22:46:42.568Z" }, + { url = "https://files.pythonhosted.org/packages/08/e5/39b930323428596990367b7953197730213d3d9d07bcedcad1d026608178/psycopg_binary-3.2.11-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa2aa5094dc962967ca0978c035b3ef90329b802501ef12a088d3bac6a55598e", size = 4411419, upload-time = "2025-10-18T22:46:47.745Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9c/97c25438d1e51ddc6a7f67990b4c59f94bc515114ada864804ccee27ef1b/psycopg_binary-3.2.11-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7744b4ed1f3b76fe37de7e9ef98014482fe74b6d3dfe1026cc4cfb4b4404e74f", size = 3867844, upload-time = "2025-10-18T22:46:53.328Z" }, + { url = "https://files.pythonhosted.org/packages/91/51/8c1e291cf4aa9982666f71a886aa782d990aa16853a42de545a0a9a871ef/psycopg_binary-3.2.11-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5f6f948ff1cd252003ff534d7b50a2b25453b4212b283a7514ff8751bdb68c37", size = 3541539, upload-time = "2025-10-18T22:46:58.993Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/e25edcdfa1111bfc5c95668b7469b5a957b40ce10cc81383688d65564826/psycopg_binary-3.2.11-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3bd2c8fb1dec6f93383fbaa561591fa3d676e079f9cb9889af17c3020a19715f", size = 3588090, upload-time = "2025-10-18T22:47:04.105Z" }, + { url = "https://files.pythonhosted.org/packages/a3/aa/f8c2f4b4c13d5680a20e5bfcd61f9e154bce26e7a2c70cb0abeade088d61/psycopg_binary-3.2.11-cp314-cp314-win_amd64.whl", hash = "sha256:c45f61202e5691090a697e599997eaffa3ec298209743caa4fd346145acabafe", size = 3006049, upload-time = "2025-10-18T22:47:07.923Z" }, ] [[package]] @@ -1351,7 +1355,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.12.0" +version = "2.11.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1359,9 +1363,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/da/b8a7ee04378a53f6fefefc0c5e05570a3ebfdfa0523a878bcd3b475683ee/pydantic-2.12.0.tar.gz", hash = "sha256:c1a077e6270dbfb37bfd8b498b3981e2bb18f68103720e51fa6c306a5a9af563", size = 814760, upload-time = "2025-10-07T15:58:03.467Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/54/ecab642b3bed45f7d5f59b38443dcb36ef50f85af192e6ece103dbfe9587/pydantic-2.11.10.tar.gz", hash = "sha256:dc280f0982fbda6c38fada4e476dc0a4f3aeaf9c6ad4c28df68a666ec3c61423", size = 788494, upload-time = "2025-10-04T10:40:41.338Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/9d/d5c855424e2e5b6b626fbc6ec514d8e655a600377ce283008b115abb7445/pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f", size = 459730, upload-time = "2025-10-07T15:58:01.576Z" }, + { url = "https://files.pythonhosted.org/packages/bd/1f/73c53fcbfb0b5a78f91176df41945ca466e71e9d9d836e5c522abda39ee7/pydantic-2.11.10-py3-none-any.whl", hash = "sha256:802a655709d49bd004c31e865ef37da30b540786a46bfce02333e0e24b5fe29a", size = 444823, upload-time = "2025-10-04T10:40:39.055Z" }, ] [package.optional-dependencies] @@ -1371,47 +1375,30 @@ email = [ [[package]] name = "pydantic-core" -version = "2.41.1" +version = "2.33.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/14/12b4a0d2b0b10d8e1d9a24ad94e7bbb43335eaf29c0c4e57860e8a30734a/pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f", size = 454870, upload-time = "2025-10-07T10:50:45.974Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/8a/6d54198536a90a37807d31a156642aae7a8e1263ed9fe6fc6245defe9332/pydantic_core-2.41.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70e790fce5f05204ef4403159857bfcd587779da78627b0babb3654f75361ebf", size = 2105825, upload-time = "2025-10-06T21:10:51.719Z" }, - { url = "https://files.pythonhosted.org/packages/4f/2e/4784fd7b22ac9c8439db25bf98ffed6853d01e7e560a346e8af821776ccc/pydantic_core-2.41.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cebf1ca35f10930612d60bd0f78adfacee824c30a880e3534ba02c207cceceb", size = 1910126, upload-time = "2025-10-06T21:10:53.145Z" }, - { url = "https://files.pythonhosted.org/packages/f3/92/31eb0748059ba5bd0aa708fb4bab9fcb211461ddcf9e90702a6542f22d0d/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:170406a37a5bc82c22c3274616bf6f17cc7df9c4a0a0a50449e559cb755db669", size = 1961472, upload-time = "2025-10-06T21:10:55.754Z" }, - { url = "https://files.pythonhosted.org/packages/ab/91/946527792275b5c4c7dde4cfa3e81241bf6900e9fee74fb1ba43e0c0f1ab/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12d4257fc9187a0ccd41b8b327d6a4e57281ab75e11dda66a9148ef2e1fb712f", size = 2063230, upload-time = "2025-10-06T21:10:57.179Z" }, - { url = "https://files.pythonhosted.org/packages/31/5d/a35c5d7b414e5c0749f1d9f0d159ee2ef4bab313f499692896b918014ee3/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a75a33b4db105dd1c8d57839e17ee12db8d5ad18209e792fa325dbb4baeb00f4", size = 2229469, upload-time = "2025-10-06T21:10:59.409Z" }, - { url = "https://files.pythonhosted.org/packages/21/4d/8713737c689afa57ecfefe38db78259d4484c97aa494979e6a9d19662584/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a589f850803a74e0fcb16a72081cafb0d72a3cdda500106942b07e76b7bf62", size = 2347986, upload-time = "2025-10-06T21:11:00.847Z" }, - { url = "https://files.pythonhosted.org/packages/f6/ec/929f9a3a5ed5cda767081494bacd32f783e707a690ce6eeb5e0730ec4986/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97939d6ea44763c456bd8a617ceada2c9b96bb5b8ab3dfa0d0827df7619014", size = 2072216, upload-time = "2025-10-06T21:11:02.43Z" }, - { url = "https://files.pythonhosted.org/packages/26/55/a33f459d4f9cc8786d9db42795dbecc84fa724b290d7d71ddc3d7155d46a/pydantic_core-2.41.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae423c65c556f09569524b80ffd11babff61f33055ef9773d7c9fabc11ed8d", size = 2193047, upload-time = "2025-10-06T21:11:03.787Z" }, - { url = "https://files.pythonhosted.org/packages/77/af/d5c6959f8b089f2185760a2779079e3c2c411bfc70ea6111f58367851629/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:4dc703015fbf8764d6a8001c327a87f1823b7328d40b47ce6000c65918ad2b4f", size = 2140613, upload-time = "2025-10-06T21:11:05.607Z" }, - { url = "https://files.pythonhosted.org/packages/58/e5/2c19bd2a14bffe7fabcf00efbfbd3ac430aaec5271b504a938ff019ac7be/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:968e4ffdfd35698a5fe659e5e44c508b53664870a8e61c8f9d24d3d145d30257", size = 2327641, upload-time = "2025-10-06T21:11:07.143Z" }, - { url = "https://files.pythonhosted.org/packages/93/ef/e0870ccda798c54e6b100aff3c4d49df5458fd64217e860cb9c3b0a403f4/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fff2b76c8e172d34771cd4d4f0ade08072385310f214f823b5a6ad4006890d32", size = 2318229, upload-time = "2025-10-06T21:11:08.73Z" }, - { url = "https://files.pythonhosted.org/packages/b1/4b/c3b991d95f5deb24d0bd52e47bcf716098fa1afe0ce2d4bd3125b38566ba/pydantic_core-2.41.1-cp313-cp313-win32.whl", hash = "sha256:a38a5263185407ceb599f2f035faf4589d57e73c7146d64f10577f6449e8171d", size = 1997911, upload-time = "2025-10-06T21:11:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ce/5c316fd62e01f8d6be1b7ee6b54273214e871772997dc2c95e204997a055/pydantic_core-2.41.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42ae7fd6760782c975897e1fdc810f483b021b32245b0105d40f6e7a3803e4b", size = 2034301, upload-time = "2025-10-06T21:11:12.113Z" }, - { url = "https://files.pythonhosted.org/packages/29/41/902640cfd6a6523194123e2c3373c60f19006447f2fb06f76de4e8466c5b/pydantic_core-2.41.1-cp313-cp313-win_arm64.whl", hash = "sha256:ad4111acc63b7384e205c27a2f15e23ac0ee21a9d77ad6f2e9cb516ec90965fb", size = 1977238, upload-time = "2025-10-06T21:11:14.1Z" }, - { url = "https://files.pythonhosted.org/packages/04/04/28b040e88c1b89d851278478842f0bdf39c7a05da9e850333c6c8cbe7dfa/pydantic_core-2.41.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:440d0df7415b50084a4ba9d870480c16c5f67c0d1d4d5119e3f70925533a0edc", size = 1875626, upload-time = "2025-10-06T21:11:15.69Z" }, - { url = "https://files.pythonhosted.org/packages/d6/58/b41dd3087505220bb58bc81be8c3e8cbc037f5710cd3c838f44f90bdd704/pydantic_core-2.41.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71eaa38d342099405dae6484216dcf1e8e4b0bebd9b44a4e08c9b43db6a2ab67", size = 2045708, upload-time = "2025-10-06T21:11:17.258Z" }, - { url = "https://files.pythonhosted.org/packages/d7/b8/760f23754e40bf6c65b94a69b22c394c24058a0ef7e2aa471d2e39219c1a/pydantic_core-2.41.1-cp313-cp313t-win_amd64.whl", hash = "sha256:555ecf7e50f1161d3f693bc49f23c82cf6cdeafc71fa37a06120772a09a38795", size = 1997171, upload-time = "2025-10-06T21:11:18.822Z" }, - { url = "https://files.pythonhosted.org/packages/41/12/cec246429ddfa2778d2d6301eca5362194dc8749ecb19e621f2f65b5090f/pydantic_core-2.41.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:05226894a26f6f27e1deb735d7308f74ef5fa3a6de3e0135bb66cdcaee88f64b", size = 2107836, upload-time = "2025-10-06T21:11:20.432Z" }, - { url = "https://files.pythonhosted.org/packages/20/39/baba47f8d8b87081302498e610aefc37142ce6a1cc98b2ab6b931a162562/pydantic_core-2.41.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:85ff7911c6c3e2fd8d3779c50925f6406d770ea58ea6dde9c230d35b52b16b4a", size = 1904449, upload-time = "2025-10-06T21:11:22.185Z" }, - { url = "https://files.pythonhosted.org/packages/50/32/9a3d87cae2c75a5178334b10358d631bd094b916a00a5993382222dbfd92/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f1f642a205687d59b52dc1a9a607f45e588f5a2e9eeae05edd80c7a8c47674", size = 1961750, upload-time = "2025-10-06T21:11:24.348Z" }, - { url = "https://files.pythonhosted.org/packages/27/42/a96c9d793a04cf2a9773bff98003bb154087b94f5530a2ce6063ecfec583/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df11c24e138876ace5ec6043e5cae925e34cf38af1a1b3d63589e8f7b5f5cdc4", size = 2063305, upload-time = "2025-10-06T21:11:26.556Z" }, - { url = "https://files.pythonhosted.org/packages/3e/8d/028c4b7d157a005b1f52c086e2d4b0067886b213c86220c1153398dbdf8f/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f0bf7f5c8f7bf345c527e8a0d72d6b26eda99c1227b0c34e7e59e181260de31", size = 2228959, upload-time = "2025-10-06T21:11:28.426Z" }, - { url = "https://files.pythonhosted.org/packages/08/f7/ee64cda8fcc9ca3f4716e6357144f9ee71166775df582a1b6b738bf6da57/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82b887a711d341c2c47352375d73b029418f55b20bd7815446d175a70effa706", size = 2345421, upload-time = "2025-10-06T21:11:30.226Z" }, - { url = "https://files.pythonhosted.org/packages/13/c0/e8ec05f0f5ee7a3656973ad9cd3bc73204af99f6512c1a4562f6fb4b3f7d/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f1d5d6bbba484bdf220c72d8ecd0be460f4bd4c5e534a541bb2cd57589fb8b", size = 2065288, upload-time = "2025-10-06T21:11:32.019Z" }, - { url = "https://files.pythonhosted.org/packages/0a/25/d77a73ff24e2e4fcea64472f5e39b0402d836da9b08b5361a734d0153023/pydantic_core-2.41.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bf1917385ebe0f968dc5c6ab1375886d56992b93ddfe6bf52bff575d03662be", size = 2189759, upload-time = "2025-10-06T21:11:33.753Z" }, - { url = "https://files.pythonhosted.org/packages/66/45/4a4ebaaae12a740552278d06fe71418c0f2869537a369a89c0e6723b341d/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4f94f3ab188f44b9a73f7295663f3ecb8f2e2dd03a69c8f2ead50d37785ecb04", size = 2140747, upload-time = "2025-10-06T21:11:35.781Z" }, - { url = "https://files.pythonhosted.org/packages/da/6d/b727ce1022f143194a36593243ff244ed5a1eb3c9122296bf7e716aa37ba/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3925446673641d37c30bd84a9d597e49f72eacee8b43322c8999fa17d5ae5bc4", size = 2327416, upload-time = "2025-10-06T21:11:37.75Z" }, - { url = "https://files.pythonhosted.org/packages/6f/8c/02df9d8506c427787059f87c6c7253435c6895e12472a652d9616ee0fc95/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:49bd51cc27adb980c7b97357ae036ce9b3c4d0bb406e84fbe16fb2d368b602a8", size = 2318138, upload-time = "2025-10-06T21:11:39.463Z" }, - { url = "https://files.pythonhosted.org/packages/98/67/0cf429a7d6802536941f430e6e3243f6d4b68f41eeea4b242372f1901794/pydantic_core-2.41.1-cp314-cp314-win32.whl", hash = "sha256:a31ca0cd0e4d12ea0df0077df2d487fc3eb9d7f96bbb13c3c5b88dcc21d05159", size = 1998429, upload-time = "2025-10-06T21:11:41.989Z" }, - { url = "https://files.pythonhosted.org/packages/38/60/742fef93de5d085022d2302a6317a2b34dbfe15258e9396a535c8a100ae7/pydantic_core-2.41.1-cp314-cp314-win_amd64.whl", hash = "sha256:1b5c4374a152e10a22175d7790e644fbd8ff58418890e07e2073ff9d4414efae", size = 2028870, upload-time = "2025-10-06T21:11:43.66Z" }, - { url = "https://files.pythonhosted.org/packages/31/38/cdd8ccb8555ef7720bd7715899bd6cfbe3c29198332710e1b61b8f5dd8b8/pydantic_core-2.41.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fee76d757639b493eb600fba668f1e17475af34c17dd61db7a47e824d464ca9", size = 1974275, upload-time = "2025-10-06T21:11:45.476Z" }, - { url = "https://files.pythonhosted.org/packages/e7/7e/8ac10ccb047dc0221aa2530ec3c7c05ab4656d4d4bd984ee85da7f3d5525/pydantic_core-2.41.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f9b9c968cfe5cd576fdd7361f47f27adeb120517e637d1b189eea1c3ece573f4", size = 1875124, upload-time = "2025-10-06T21:11:47.591Z" }, - { url = "https://files.pythonhosted.org/packages/c3/e4/7d9791efeb9c7d97e7268f8d20e0da24d03438a7fa7163ab58f1073ba968/pydantic_core-2.41.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ebc7ab67b856384aba09ed74e3e977dded40e693de18a4f197c67d0d4e6d8e", size = 2043075, upload-time = "2025-10-06T21:11:49.542Z" }, - { url = "https://files.pythonhosted.org/packages/2d/c3/3f6e6b2342ac11ac8cd5cb56e24c7b14afa27c010e82a765ffa5f771884a/pydantic_core-2.41.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8ae0dc57b62a762985bc7fbf636be3412394acc0ddb4ade07fe104230f1b9762", size = 1995341, upload-time = "2025-10-06T21:11:51.497Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, ] [[package]] @@ -1795,14 +1782,14 @@ wheels = [ [[package]] name = "requests-file" -version = "2.1.0" +version = "3.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/97/bf44e6c6bd8ddbb99943baf7ba8b1a8485bcd2fe0e55e5708d7fee4ff1ae/requests_file-2.1.0.tar.gz", hash = "sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658", size = 6891, upload-time = "2024-05-21T16:28:00.24Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3c/f8/5dc70102e4d337063452c82e1f0d95e39abfe67aa222ed8a5ddeb9df8de8/requests_file-3.0.1.tar.gz", hash = "sha256:f14243d7796c588f3521bd423c5dea2ee4cc730e54a3cac9574d78aca1272576", size = 6967, upload-time = "2025-10-20T18:56:42.279Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/25/dd878a121fcfdf38f52850f11c512e13ec87c2ea72385933818e5b6c15ce/requests_file-2.1.0-py2.py3-none-any.whl", hash = "sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c", size = 4244, upload-time = "2024-05-21T16:27:57.733Z" }, + { url = "https://files.pythonhosted.org/packages/e1/d5/de8f089119205a09da657ed4784c584ede8381a0ce6821212a6d4ca47054/requests_file-3.0.1-py2.py3-none-any.whl", hash = "sha256:d0f5eb94353986d998f80ac63c7f146a307728be051d4d1cd390dbdb59c10fa2", size = 4514, upload-time = "2025-10-20T18:56:41.184Z" }, ] [[package]] @@ -1834,38 +1821,40 @@ wheels = [ [[package]] name = "rignore" -version = "0.7.0" +version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/46/e5ef3423a3746f91d3a3d9a68c499fde983be7dbab7d874efa8d3bb139ba/rignore-0.7.0.tar.gz", hash = "sha256:cfe6a2cbec855b440d7550d53e670246fce43ca5847e46557b6d4577c9cdb540", size = 12796, upload-time = "2025-10-02T13:26:22.194Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/85/cd1441043c5ed13e671153af260c5f328042ebfb87aa28849367602206f2/rignore-0.7.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:190e469db68112c4027a7a126facfd80ce353374ff208c585ca7dacc75de0472", size = 880474, upload-time = "2025-10-02T13:25:08.111Z" }, - { url = "https://files.pythonhosted.org/packages/f4/07/d5b9593cb05593718508308543a8fbee75998a7489cf4f4b489d2632bd4a/rignore-0.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0a43f6fabf46ed8e96fbf2861187362e513960c2a8200c35242981bd36ef8b96", size = 811882, upload-time = "2025-10-02T13:24:56.599Z" }, - { url = "https://files.pythonhosted.org/packages/aa/67/b82b2704660c280061d8bc90bc91092622309f78e20c9e3321f45f88cd4e/rignore-0.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b89a59e5291805eca3c3317a55fcd2a579e9ee1184511660078a398182463deb", size = 892043, upload-time = "2025-10-02T13:23:22.326Z" }, - { url = "https://files.pythonhosted.org/packages/8b/7e/e91a1899a06882cd8a7acc3025c51b9f830971b193bd6b72e34254ed7733/rignore-0.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a155f36be847c05c800e0218e9ac04946ba44bf077e1f11dc024ca9e1f7a727", size = 865404, upload-time = "2025-10-02T13:23:40.085Z" }, - { url = "https://files.pythonhosted.org/packages/91/2c/68487538a2d2d7e0e1ca1051d143af690211314e22cbed58a245e816ebaf/rignore-0.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dba075135ac3cda5f3236b4f03f82bbcd97454a908631ad3da93aae1e7390b17", size = 1167661, upload-time = "2025-10-02T13:23:57.578Z" }, - { url = "https://files.pythonhosted.org/packages/b4/39/8498ac13fb710a1920526480f9476aaeaaaa20c522a027d07513929ba9d9/rignore-0.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8525b8c31f36dc9fbcb474ef58d654f6404b19b6110b7f5df332e58e657a4aa8", size = 936272, upload-time = "2025-10-02T13:24:13.414Z" }, - { url = "https://files.pythonhosted.org/packages/55/1a/38b92fde209931611dcff0db59bd5656a325ba58d368d4e50f1e711fdd16/rignore-0.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0428b64d8b02ad83fc0a2505ded0e9064cac97df7aa1dffc9c7558b56429912", size = 950552, upload-time = "2025-10-02T13:24:43.263Z" }, - { url = "https://files.pythonhosted.org/packages/e3/01/f59f38ae1b879309b0151b1ed0dd82880e1d3759f91bfdaa570730672308/rignore-0.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab1db960a64835ec3ed541951821bfc38f30dfbd6ebd990f7d039d0c54ff957", size = 974407, upload-time = "2025-10-02T13:24:30.618Z" }, - { url = "https://files.pythonhosted.org/packages/6e/67/de92fdc09dc1a622abb6d1b2678e940d24de2a07c60d193126eb52a7e8ea/rignore-0.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3749711b1e50fb5b28b55784e159a3b8209ecc72d01cc1511c05bc3a23b4a063", size = 1072865, upload-time = "2025-10-02T13:25:20.451Z" }, - { url = "https://files.pythonhosted.org/packages/65/bb/75fbef03cf56b0918880cb3b922da83d6546309566be60f6c6b451f7221b/rignore-0.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:57240739c786f897f89e29c05e529291ee1b477df9f6b29b774403a23a169fe2", size = 1129007, upload-time = "2025-10-02T13:25:36.837Z" }, - { url = "https://files.pythonhosted.org/packages/ec/24/4d591d45a8994fb4afaefa22e356d69948726c9ccba0cfd76c82509aedc2/rignore-0.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6b70581286acd5f96ce11efd209bfe9261108586e1a948cc558fc3f58ba5bf5f", size = 1106827, upload-time = "2025-10-02T13:25:52.964Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b3/b614d54fa1f1c7621aeb20b2841cd980288ad9d7d61407fc4595d5c5f132/rignore-0.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33fb6e4cba1b798f1328e889b4bf2341894d82e3be42bb3513b4e0fe38788538", size = 1115328, upload-time = "2025-10-02T13:26:10.947Z" }, - { url = "https://files.pythonhosted.org/packages/83/22/ea0b3e30e230b2d2222e1ee18e20316c8297088f4cc6a6ea2ee6cb34f595/rignore-0.7.0-cp313-cp313-win32.whl", hash = "sha256:119f0497fb4776cddc663ee8f35085ce00758bd423221ba1e8222a816e10cf5e", size = 636896, upload-time = "2025-10-02T13:26:40.3Z" }, - { url = "https://files.pythonhosted.org/packages/79/16/f55b3db13f6fff408fde348d2a726d3b4ba06ed55dce8ff119e374ce3005/rignore-0.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:fb06e11dda689be138909f53639f0baa8d7c6be4d76ca9ec316382ccf3517469", size = 716519, upload-time = "2025-10-02T13:26:28.51Z" }, - { url = "https://files.pythonhosted.org/packages/69/db/8c20a7b59abb21d3d20d387656b6759cd5890fa68185064fe8899f942a4b/rignore-0.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f2255821ab4bc34fa129a94535f5d0d88b164940b25d0a3b26ebd41d99f1a9f", size = 890684, upload-time = "2025-10-02T13:23:23.761Z" }, - { url = "https://files.pythonhosted.org/packages/45/a0/ae5ca63aed23f64dcd740f55ee6432037af5c09d25efaf79dc052a4a51ff/rignore-0.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b57efcbbc1510f8ce831a5e19fb1fe9dd329bb246c4e4f8a09bf1c06687b0331", size = 865174, upload-time = "2025-10-02T13:23:41.948Z" }, - { url = "https://files.pythonhosted.org/packages/ae/27/5aff661e792efbffda689f0d3fa91ea36f2e0d4bcca3b02f70ae95ea96da/rignore-0.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ead4bc2baceeccdfeb82cb70ba8f70fdb6dc1e58976f805f9d0d19b9ee915f0", size = 1165293, upload-time = "2025-10-02T13:23:59.238Z" }, - { url = "https://files.pythonhosted.org/packages/cb/df/13de7ce5ba2a58c724ef202310408729941c262179389df5e90cb9a41381/rignore-0.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4f0a8996437a22df0faf2844d65ec91d41176b9d4e7357abee42baa39dc996ae", size = 936093, upload-time = "2025-10-02T13:24:15.057Z" }, - { url = "https://files.pythonhosted.org/packages/c3/63/4ea42bc454db8499906c8d075a7a0053b7fd381b85f3bcc857e68a8b8b23/rignore-0.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:cb17ef4a413444fccbd57e1b4a3870f1320951b81f1b7007af9c70e1a5bc2897", size = 1071518, upload-time = "2025-10-02T13:25:22.076Z" }, - { url = "https://files.pythonhosted.org/packages/a3/a7/7400a4343d1b5a1345a98846c6fd7768ff13890d207fce79d690c7fd7798/rignore-0.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:b12b316adf6cf64f9d22bd690b2aa019a37335a1f632a0da7fb15a423cb64080", size = 1128403, upload-time = "2025-10-02T13:25:38.394Z" }, - { url = "https://files.pythonhosted.org/packages/45/8b/ce8ff27336a86bad47bbf011f8f7fb0b82b559ee4a0d6a4815ee3555ef56/rignore-0.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:dba8181d999387c17dd6cce5fd7f0009376ca8623d2d86842d034b18d83dc768", size = 1105552, upload-time = "2025-10-02T13:25:54.511Z" }, - { url = "https://files.pythonhosted.org/packages/8c/e2/7925b564d853c7057f150a7f2f384400422ed30f7b7baf2fde5849562381/rignore-0.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:04a3d4513cdd184f4f849ae8d6407a169cca543a2c4dd69bfc42e67cb0155504", size = 1114826, upload-time = "2025-10-02T13:26:12.56Z" }, - { url = "https://files.pythonhosted.org/packages/c4/34/c42ccdd81143d38d99e45b965e4040a1ef6c07a365ad205dd94b6d16c794/rignore-0.7.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:a296bc26b713aacd0f31702e7d89426ba6240abdbf01b2b18daeeaeaa782f475", size = 879718, upload-time = "2025-10-02T13:25:09.62Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ba/f522adf949d2b581a0a1e488a79577631ed6661fdc12e80d4182ed655036/rignore-0.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7f71807ed0bc1542860a8fa1615a0d93f3d5a22dde1066e9f50d7270bc60686", size = 810391, upload-time = "2025-10-02T13:24:58.144Z" }, - { url = "https://files.pythonhosted.org/packages/f2/82/935bffa4ad7d9560541daaca7ba0e4ee9b0b9a6370ab9518cf9c991087bb/rignore-0.7.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7e6ff54399ddb650f4e4dc74b325766e7607967a49b868326e9687fc3642620", size = 950261, upload-time = "2025-10-02T13:24:45.121Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0e/22abda23cc6d20901262fcfea50c25ed66ca6e1a5dc610d338df4ca10407/rignore-0.7.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:09dfad3ca450b3967533c6b1a2c7c0228c63c518f619ff342df5f9c3ed978b66", size = 974258, upload-time = "2025-10-02T13:24:32.44Z" }, - { url = "https://files.pythonhosted.org/packages/ed/8d/0ba2c712723fdda62125087d00dcdad93102876d4e3fa5adbb99f0b859c3/rignore-0.7.0-cp314-cp314-win32.whl", hash = "sha256:2850718cfb1caece6b7ac19a524c7905a8d0c6627b0d0f4e81798e20b6c75078", size = 637403, upload-time = "2025-10-02T13:26:41.814Z" }, - { url = "https://files.pythonhosted.org/packages/1c/63/0d7df1237c6353d1a85d8a0bc1797ac766c68e8bc6fbca241db74124eb61/rignore-0.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2401637dc8ab074f5e642295f8225d2572db395ae504ffc272a8d21e9fe77b2c", size = 717404, upload-time = "2025-10-02T13:26:29.936Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/01/1a/4e407524cf97ed42a9c77d3cc31b12dd5fb2ce542f174ff7cf78ea0ca293/rignore-0.7.1.tar.gz", hash = "sha256:67bb99d57d0bab0c473261561f98f118f7c9838a06de222338ed8f2b95ed84b4", size = 15437, upload-time = "2025-10-15T20:59:08.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f8/99145d7ee439db898709b9a7e913d42ed3a6ff679c50a163bae373f07276/rignore-0.7.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:cb6c993b22d7c88eeadc4fed2957be688b6c5f98d4a9b86d3a5057f4a17ea5bd", size = 881743, upload-time = "2025-10-15T20:58:09.804Z" }, + { url = "https://files.pythonhosted.org/packages/fa/db/aea84354518a24578c77d8fec2f42c065520b48ba5bded9d8eca9e46fefd/rignore-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:32da28b0e0434b88134f8d97f22afe6bd1e2a103278a726809e2d8da8426b33f", size = 814397, upload-time = "2025-10-15T20:58:00.071Z" }, + { url = "https://files.pythonhosted.org/packages/12/0b/116afdee4093f0ccd3c4e7b6840d3699ea2a34c1ae6d1dd4d7d9d0adc65b/rignore-0.7.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:401d52a0a1c5eae342b2c7b4091206e1ce70de54e85c8c8f0ea3309765a62d60", size = 893431, upload-time = "2025-10-15T20:56:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/52/b5/66778c7cbb8e2c6f4ca6f2f59067aa01632b913741c4aa46b163dc4c8f8c/rignore-0.7.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9ffcfbef75656243cfdcdd495b0ea0b71980b76af343b1bf3aed61a78db3f145", size = 867220, upload-time = "2025-10-15T20:56:58.931Z" }, + { url = "https://files.pythonhosted.org/packages/6e/da/bdd6de52941391f0056295c6904c45e1f8667df754b17fe880d0a663d941/rignore-0.7.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e89efa2ad36a9206ed30219eb1a8783a0722ae8b6d68390ae854e5f5ceab6ff", size = 1169076, upload-time = "2025-10-15T20:57:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/0e/8d/d7d4bfbae28e340a6afe850809a020a31c2364fc0ee8105be4ec0841b20a/rignore-0.7.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f6191d7f52894ee65a879f022329011e31cc41f98739ff184cd3f256a3f0711", size = 937738, upload-time = "2025-10-15T20:57:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/d8/b1/1d3f88aaf3cc6f4e31d1d72eb261eff3418dabd2677c83653b7574e7947a/rignore-0.7.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:873a8e84b4342534b9e283f7c17dc39c295edcdc686dfa395ddca3628316931b", size = 951791, upload-time = "2025-10-15T20:57:49.574Z" }, + { url = "https://files.pythonhosted.org/packages/90/7f/033631f29af972bc4f69e241ab188d21fbc4665ad67879c77bc984009550/rignore-0.7.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65443a6a5efd184d21538816282c78c4787a8a5f73c243ab87cbbb6f313a623d", size = 977580, upload-time = "2025-10-15T20:57:39.063Z" }, + { url = "https://files.pythonhosted.org/packages/c7/38/6f963926b769365a803ec17d448a4fc9c2dbad9c1a1bf73c28088021c2fc/rignore-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d6cafca0b422c0d57ce617fed3831e6639dc151653b98396af919f8eb3ba9e2b", size = 1074486, upload-time = "2025-10-15T20:58:18.505Z" }, + { url = "https://files.pythonhosted.org/packages/74/d2/a1c1e2cd3e43f6433d3ecb8d947e1ed684c261fa2e7b2f6b8827c3bf18d1/rignore-0.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:1f731b018b5b5a93d7b4a0f4e43e5fcbd6cf25e97cec265392f9dd8d10916e5c", size = 1131024, upload-time = "2025-10-15T20:58:32.075Z" }, + { url = "https://files.pythonhosted.org/packages/93/22/b7dd8312aa98211df1f10a6cd2a3005e72cd4ac5c125fd064c7e58394205/rignore-0.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3be78b1ab9fa1c0eac822a69a7799a2261ce06e4d548374093c4c64d796d7d8", size = 1109625, upload-time = "2025-10-15T20:58:46.077Z" }, + { url = "https://files.pythonhosted.org/packages/f7/65/dd31859304bd71ad72f71e2bf5f18e6f0043cc75394ead8c0d752ab580ad/rignore-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d8c3b77ae1a24b09a6d38e07d180f362e47b970c767d2e22417b03d95685cb9d", size = 1117466, upload-time = "2025-10-15T20:58:59.102Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d7/e83241e1b0a6caef1e37586d5b2edb0227478d038675e4e6e1cd748c08ce/rignore-0.7.1-cp313-cp313-win32.whl", hash = "sha256:c01cc8c5d7099d35a7fd00e174948986d4f2cfb6b7fe2923b0b801b1a4741b37", size = 635266, upload-time = "2025-10-15T20:59:28.782Z" }, + { url = "https://files.pythonhosted.org/packages/95/e5/c2ce66a71cfc44010a238a61339cae7469adc17306025796884672784b4c/rignore-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:5dd0de4a7d38a49b9d85f332d129b4ca4a29eef5667d4c7bf503e767cf9e2ec4", size = 718048, upload-time = "2025-10-15T20:59:19.312Z" }, + { url = "https://files.pythonhosted.org/packages/ba/fb/b92aa591e247f6258997163e8b1844c9b799371fbfdfd29533e203df06b9/rignore-0.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:4a4c57b75ec758fb31ad1abab4c77810ea417e9d33bdf2f38cf9e6db556eebcb", size = 647790, upload-time = "2025-10-15T20:59:12.408Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d3/b6c5764d3dcaf47de7f0e408dcb4a1a17d4ce3bb1b0aa9a346e221e3c5a1/rignore-0.7.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb7df83a41213069195436e9c1a433a6df85c089ce4be406d070a4db0ee3897", size = 892938, upload-time = "2025-10-15T20:56:46.559Z" }, + { url = "https://files.pythonhosted.org/packages/48/6a/4d8ae9af9936a061dacda0d8f638cd63571ff93e4eb28e0159db6c4dc009/rignore-0.7.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:30d9c9a93a266d1f384465d626178f49d0da4d1a0cf739f15151cdf2eb500e53", size = 867312, upload-time = "2025-10-15T20:57:00.083Z" }, + { url = "https://files.pythonhosted.org/packages/9b/88/cb243662a0b523b4350db1c7c3adee87004af90e9b26100e84c7e13b93cc/rignore-0.7.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7e83c68f557d793b4cc7aac943f3b23631469e1bc5b02e63626d0b008be01cd1", size = 1166871, upload-time = "2025-10-15T20:57:13.618Z" }, + { url = "https://files.pythonhosted.org/packages/f6/0a/da28a3f3e8ab1829180f3a7af5b601b04bab1d833e31a74fee78a2d3f5c3/rignore-0.7.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:682a6efe3f84af4b1100d4c68f0a345f490af74fd9d18346ebf67da9a3b96b08", size = 937964, upload-time = "2025-10-15T20:57:27.054Z" }, + { url = "https://files.pythonhosted.org/packages/c6/2e/f55d0759c6cf48d8fabc62d8924ce58dca81f5c370c0abdcc7cc8176210d/rignore-0.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:736b6aa3e3dfda2b1404b6f9a9d6f67e2a89f184179e9e5b629198df7c22f9c6", size = 1073720, upload-time = "2025-10-15T20:58:20.833Z" }, + { url = "https://files.pythonhosted.org/packages/c3/aa/8698caf5eb1824f8cae08cd3a296bc7f6f46e7bb539a4dd60c6a7a9f5ca2/rignore-0.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eed55292d949e99f29cd4f1ae6ddc2562428a3e74f6f4f6b8658f1d5113ffbd5", size = 1130545, upload-time = "2025-10-15T20:58:33.709Z" }, + { url = "https://files.pythonhosted.org/packages/f5/88/89abacdc122f4a0d069d12ebbd87693253f08f19457b77f030c0c6cba316/rignore-0.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:93ce054754857e37f15fe6768fd28e5450a52c7bbdb00e215100b092281ed123", size = 1108570, upload-time = "2025-10-15T20:58:47.438Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4b/a815624ff1f2420ff29be1ffa2ea5204a69d9a9738fe5a6638fcd1069347/rignore-0.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:447004c774083e4f9cddf0aefcb80b12264f23e28c37918fb709917c2aabd00d", size = 1116940, upload-time = "2025-10-15T20:59:00.581Z" }, + { url = "https://files.pythonhosted.org/packages/43/63/3464fe5855fc37689d7bdd7b4b7ea0d008a8a58738bc0d68b0b5fa6dcf28/rignore-0.7.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:322ac35f2431dd2e80518200e31af1985689dfa7658003ae40012bf3d3e9f0dd", size = 880536, upload-time = "2025-10-15T20:58:11.286Z" }, + { url = "https://files.pythonhosted.org/packages/63/c3/c37469643baeb04c58db2713dc268f582974c71f3936f7d989610b344fca/rignore-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2d38e282e4b917fb6108198564b018f90de57bb6209aadf9ff39434d4709a650", size = 814741, upload-time = "2025-10-15T20:58:01.228Z" }, + { url = "https://files.pythonhosted.org/packages/76/6c/57fa917c7515db3b72a9c3a6377dc806282e6db390ace68cda29bd73774e/rignore-0.7.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89ad7373ec1e7b519a6f07dbcfca38024ba45f5e44df79ee0da4e4c817648a50", size = 951257, upload-time = "2025-10-15T20:57:50.779Z" }, + { url = "https://files.pythonhosted.org/packages/b6/58/b64fb42d6a73937a93c5f060e2720decde4d2b4a7a27fc3b69e69c397358/rignore-0.7.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ff94b215b4fe1d81e45b29dc259145fd8aaf40e7b1057f020890cd12db566e4e", size = 977468, upload-time = "2025-10-15T20:57:40.291Z" }, + { url = "https://files.pythonhosted.org/packages/22/54/5b9e60ad6ea7ef654d2607936be312ce78615e011b3461d4b1d161f031c0/rignore-0.7.1-cp314-cp314-win32.whl", hash = "sha256:f49ecef68b5cb99d1212ebe332cbb2851fb2c93672d3b1d372b0fbf475eeb172", size = 635618, upload-time = "2025-10-15T20:59:29.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/50/67137617cbe3e53cbf34d21dad49e153f731797e07261f3b00572a49e69d/rignore-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:3f55593d3bbcae3c108d546e8776e51ecb61d1d79bbb02016acf29d136813835", size = 717951, upload-time = "2025-10-15T20:59:20.519Z" }, + { url = "https://files.pythonhosted.org/packages/77/19/dd556e97354ad541b4f7f113e28503865777d6edd940c147f052dc7b8f04/rignore-0.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:60745773b5278fa5f20232fbfb148d74ad9fb27ae8a5097d3cbd5d7cc922d7f7", size = 647796, upload-time = "2025-10-15T20:59:13.724Z" }, ] [[package]] @@ -1882,28 +1871,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/b9/9bd84453ed6dd04688de9b3f3a4146a1698e8faae2ceeccce4e14c67ae17/ruff-0.14.0.tar.gz", hash = "sha256:62ec8969b7510f77945df916de15da55311fade8d6050995ff7f680afe582c57", size = 5452071, upload-time = "2025-10-07T18:21:55.763Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/4e/79d463a5f80654e93fa653ebfb98e0becc3f0e7cf6219c9ddedf1e197072/ruff-0.14.0-py3-none-linux_armv6l.whl", hash = "sha256:58e15bffa7054299becf4bab8a1187062c6f8cafbe9f6e39e0d5aface455d6b3", size = 12494532, upload-time = "2025-10-07T18:21:00.373Z" }, - { url = "https://files.pythonhosted.org/packages/ee/40/e2392f445ed8e02aa6105d49db4bfff01957379064c30f4811c3bf38aece/ruff-0.14.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:838d1b065f4df676b7c9957992f2304e41ead7a50a568185efd404297d5701e8", size = 13160768, upload-time = "2025-10-07T18:21:04.73Z" }, - { url = "https://files.pythonhosted.org/packages/75/da/2a656ea7c6b9bd14c7209918268dd40e1e6cea65f4bb9880eaaa43b055cd/ruff-0.14.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:703799d059ba50f745605b04638fa7e9682cc3da084b2092feee63500ff3d9b8", size = 12363376, upload-time = "2025-10-07T18:21:07.833Z" }, - { url = "https://files.pythonhosted.org/packages/42/e2/1ffef5a1875add82416ff388fcb7ea8b22a53be67a638487937aea81af27/ruff-0.14.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ba9a8925e90f861502f7d974cc60e18ca29c72bb0ee8bfeabb6ade35a3abde7", size = 12608055, upload-time = "2025-10-07T18:21:10.72Z" }, - { url = "https://files.pythonhosted.org/packages/4a/32/986725199d7cee510d9f1dfdf95bf1efc5fa9dd714d0d85c1fb1f6be3bc3/ruff-0.14.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e41f785498bd200ffc276eb9e1570c019c1d907b07cfb081092c8ad51975bbe7", size = 12318544, upload-time = "2025-10-07T18:21:13.741Z" }, - { url = "https://files.pythonhosted.org/packages/9a/ed/4969cefd53315164c94eaf4da7cfba1f267dc275b0abdd593d11c90829a3/ruff-0.14.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30a58c087aef4584c193aebf2700f0fbcfc1e77b89c7385e3139956fa90434e2", size = 14001280, upload-time = "2025-10-07T18:21:16.411Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ad/96c1fc9f8854c37681c9613d825925c7f24ca1acfc62a4eb3896b50bacd2/ruff-0.14.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f8d07350bc7af0a5ce8812b7d5c1a7293cf02476752f23fdfc500d24b79b783c", size = 15027286, upload-time = "2025-10-07T18:21:19.577Z" }, - { url = "https://files.pythonhosted.org/packages/b3/00/1426978f97df4fe331074baf69615f579dc4e7c37bb4c6f57c2aad80c87f/ruff-0.14.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eec3bbbf3a7d5482b5c1f42d5fc972774d71d107d447919fca620b0be3e3b75e", size = 14451506, upload-time = "2025-10-07T18:21:22.779Z" }, - { url = "https://files.pythonhosted.org/packages/58/d5/9c1cea6e493c0cf0647674cca26b579ea9d2a213b74b5c195fbeb9678e15/ruff-0.14.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16b68e183a0e28e5c176d51004aaa40559e8f90065a10a559176713fcf435206", size = 13437384, upload-time = "2025-10-07T18:21:25.758Z" }, - { url = "https://files.pythonhosted.org/packages/29/b4/4cd6a4331e999fc05d9d77729c95503f99eae3ba1160469f2b64866964e3/ruff-0.14.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb732d17db2e945cfcbbc52af0143eda1da36ca8ae25083dd4f66f1542fdf82e", size = 13447976, upload-time = "2025-10-07T18:21:28.83Z" }, - { url = "https://files.pythonhosted.org/packages/3b/c0/ac42f546d07e4f49f62332576cb845d45c67cf5610d1851254e341d563b6/ruff-0.14.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:c958f66ab884b7873e72df38dcabee03d556a8f2ee1b8538ee1c2bbd619883dd", size = 13682850, upload-time = "2025-10-07T18:21:31.842Z" }, - { url = "https://files.pythonhosted.org/packages/5f/c4/4b0c9bcadd45b4c29fe1af9c5d1dc0ca87b4021665dfbe1c4688d407aa20/ruff-0.14.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7eb0499a2e01f6e0c285afc5bac43ab380cbfc17cd43a2e1dd10ec97d6f2c42d", size = 12449825, upload-time = "2025-10-07T18:21:35.074Z" }, - { url = "https://files.pythonhosted.org/packages/4b/a8/e2e76288e6c16540fa820d148d83e55f15e994d852485f221b9524514730/ruff-0.14.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c63b2d99fafa05efca0ab198fd48fa6030d57e4423df3f18e03aa62518c565f", size = 12272599, upload-time = "2025-10-07T18:21:38.08Z" }, - { url = "https://files.pythonhosted.org/packages/18/14/e2815d8eff847391af632b22422b8207704222ff575dec8d044f9ab779b2/ruff-0.14.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:668fce701b7a222f3f5327f86909db2bbe99c30877c8001ff934c5413812ac02", size = 13193828, upload-time = "2025-10-07T18:21:41.216Z" }, - { url = "https://files.pythonhosted.org/packages/44/c6/61ccc2987cf0aecc588ff8f3212dea64840770e60d78f5606cd7dc34de32/ruff-0.14.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a86bf575e05cb68dcb34e4c7dfe1064d44d3f0c04bbc0491949092192b515296", size = 13628617, upload-time = "2025-10-07T18:21:44.04Z" }, - { url = "https://files.pythonhosted.org/packages/73/e6/03b882225a1b0627e75339b420883dc3c90707a8917d2284abef7a58d317/ruff-0.14.0-py3-none-win32.whl", hash = "sha256:7450a243d7125d1c032cb4b93d9625dea46c8c42b4f06c6b709baac168e10543", size = 12367872, upload-time = "2025-10-07T18:21:46.67Z" }, - { url = "https://files.pythonhosted.org/packages/41/77/56cf9cf01ea0bfcc662de72540812e5ba8e9563f33ef3d37ab2174892c47/ruff-0.14.0-py3-none-win_amd64.whl", hash = "sha256:ea95da28cd874c4d9c922b39381cbd69cb7e7b49c21b8152b014bd4f52acddc2", size = 13464628, upload-time = "2025-10-07T18:21:50.318Z" }, - { url = "https://files.pythonhosted.org/packages/c6/2a/65880dfd0e13f7f13a775998f34703674a4554906167dce02daf7865b954/ruff-0.14.0-py3-none-win_arm64.whl", hash = "sha256:f42c9495f5c13ff841b1da4cb3c2a42075409592825dada7c5885c2c844ac730", size = 12565142, upload-time = "2025-10-07T18:21:53.577Z" }, +version = "0.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/58/6ca66896635352812de66f71cdf9ff86b3a4f79071ca5730088c0cd0fc8d/ruff-0.14.1.tar.gz", hash = "sha256:1dd86253060c4772867c61791588627320abcb6ed1577a90ef432ee319729b69", size = 5513429, upload-time = "2025-10-16T18:05:41.766Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/39/9cc5ab181478d7a18adc1c1e051a84ee02bec94eb9bdfd35643d7c74ca31/ruff-0.14.1-py3-none-linux_armv6l.whl", hash = "sha256:083bfc1f30f4a391ae09c6f4f99d83074416b471775b59288956f5bc18e82f8b", size = 12445415, upload-time = "2025-10-16T18:04:48.227Z" }, + { url = "https://files.pythonhosted.org/packages/ef/2e/1226961855ccd697255988f5a2474890ac7c5863b080b15bd038df820818/ruff-0.14.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f6fa757cd717f791009f7669fefb09121cc5f7d9bd0ef211371fad68c2b8b224", size = 12784267, upload-time = "2025-10-16T18:04:52.515Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ea/fd9e95863124ed159cd0667ec98449ae461de94acda7101f1acb6066da00/ruff-0.14.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d6191903d39ac156921398e9c86b7354d15e3c93772e7dbf26c9fcae59ceccd5", size = 11781872, upload-time = "2025-10-16T18:04:55.396Z" }, + { url = "https://files.pythonhosted.org/packages/1e/5a/e890f7338ff537dba4589a5e02c51baa63020acfb7c8cbbaea4831562c96/ruff-0.14.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed04f0e04f7a4587244e5c9d7df50e6b5bf2705d75059f409a6421c593a35896", size = 12226558, upload-time = "2025-10-16T18:04:58.166Z" }, + { url = "https://files.pythonhosted.org/packages/a6/7a/8ab5c3377f5bf31e167b73651841217542bcc7aa1c19e83030835cc25204/ruff-0.14.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5c9e6cf6cd4acae0febbce29497accd3632fe2025c0c583c8b87e8dbdeae5f61", size = 12187898, upload-time = "2025-10-16T18:05:01.455Z" }, + { url = "https://files.pythonhosted.org/packages/48/8d/ba7c33aa55406955fc124e62c8259791c3d42e3075a71710fdff9375134f/ruff-0.14.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fa2458527794ecdfbe45f654e42c61f2503a230545a91af839653a0a93dbc6", size = 12939168, upload-time = "2025-10-16T18:05:04.397Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c2/70783f612b50f66d083380e68cbd1696739d88e9b4f6164230375532c637/ruff-0.14.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:39f1c392244e338b21d42ab29b8a6392a722c5090032eb49bb4d6defcdb34345", size = 14386942, upload-time = "2025-10-16T18:05:07.102Z" }, + { url = "https://files.pythonhosted.org/packages/48/44/cd7abb9c776b66d332119d67f96acf15830d120f5b884598a36d9d3f4d83/ruff-0.14.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7382fa12a26cce1f95070ce450946bec357727aaa428983036362579eadcc5cf", size = 13990622, upload-time = "2025-10-16T18:05:09.882Z" }, + { url = "https://files.pythonhosted.org/packages/eb/56/4259b696db12ac152fe472764b4f78bbdd9b477afd9bc3a6d53c01300b37/ruff-0.14.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd0bf2be3ae8521e1093a487c4aa3b455882f139787770698530d28ed3fbb37c", size = 13431143, upload-time = "2025-10-16T18:05:13.46Z" }, + { url = "https://files.pythonhosted.org/packages/e0/35/266a80d0eb97bd224b3265b9437bd89dde0dcf4faf299db1212e81824e7e/ruff-0.14.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cabcaa9ccf8089fb4fdb78d17cc0e28241520f50f4c2e88cb6261ed083d85151", size = 13132844, upload-time = "2025-10-16T18:05:16.1Z" }, + { url = "https://files.pythonhosted.org/packages/65/6e/d31ce218acc11a8d91ef208e002a31acf315061a85132f94f3df7a252b18/ruff-0.14.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:747d583400f6125ec11a4c14d1c8474bf75d8b419ad22a111a537ec1a952d192", size = 13401241, upload-time = "2025-10-16T18:05:19.395Z" }, + { url = "https://files.pythonhosted.org/packages/9f/b5/dbc4221bf0b03774b3b2f0d47f39e848d30664157c15b965a14d890637d2/ruff-0.14.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5a6e74c0efd78515a1d13acbfe6c90f0f5bd822aa56b4a6d43a9ffb2ae6e56cd", size = 12132476, upload-time = "2025-10-16T18:05:22.163Z" }, + { url = "https://files.pythonhosted.org/packages/98/4b/ac99194e790ccd092d6a8b5f341f34b6e597d698e3077c032c502d75ea84/ruff-0.14.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0ea6a864d2fb41a4b6d5b456ed164302a0d96f4daac630aeba829abfb059d020", size = 12139749, upload-time = "2025-10-16T18:05:25.162Z" }, + { url = "https://files.pythonhosted.org/packages/47/26/7df917462c3bb5004e6fdfcc505a49e90bcd8a34c54a051953118c00b53a/ruff-0.14.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0826b8764f94229604fa255918d1cc45e583e38c21c203248b0bfc9a0e930be5", size = 12544758, upload-time = "2025-10-16T18:05:28.018Z" }, + { url = "https://files.pythonhosted.org/packages/64/d0/81e7f0648e9764ad9b51dd4be5e5dac3fcfff9602428ccbae288a39c2c22/ruff-0.14.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cbc52160465913a1a3f424c81c62ac8096b6a491468e7d872cb9444a860bc33d", size = 13221811, upload-time = "2025-10-16T18:05:30.707Z" }, + { url = "https://files.pythonhosted.org/packages/c3/07/3c45562c67933cc35f6d5df4ca77dabbcd88fddaca0d6b8371693d29fd56/ruff-0.14.1-py3-none-win32.whl", hash = "sha256:e037ea374aaaff4103240ae79168c0945ae3d5ae8db190603de3b4012bd1def6", size = 12319467, upload-time = "2025-10-16T18:05:33.261Z" }, + { url = "https://files.pythonhosted.org/packages/02/88/0ee4ca507d4aa05f67e292d2e5eb0b3e358fbcfe527554a2eda9ac422d6b/ruff-0.14.1-py3-none-win_amd64.whl", hash = "sha256:59d599cdff9c7f925a017f6f2c256c908b094e55967f93f2821b1439928746a1", size = 13401123, upload-time = "2025-10-16T18:05:35.984Z" }, + { url = "https://files.pythonhosted.org/packages/b8/81/4b6387be7014858d924b843530e1b2a8e531846807516e9bea2ee0936bf7/ruff-0.14.1-py3-none-win_arm64.whl", hash = "sha256:e3b443c4c9f16ae850906b8d0a707b2a4c16f8d2f0a7fe65c475c5886665ce44", size = 12436636, upload-time = "2025-10-16T18:05:38.995Z" }, ] [[package]] @@ -1920,15 +1909,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.41.0" +version = "2.42.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/47/aea50a61d85bc07a34e6e7145aad7bd96c5671a86a32618059bad0cbc73b/sentry_sdk-2.41.0.tar.gz", hash = "sha256:e7af3f4d7f8bac4c56fbaf95adb0d111f061cce58d5df91cfcd4e69782759b10", size = 343942, upload-time = "2025-10-09T14:12:21.132Z" } +sdist = { url = "https://files.pythonhosted.org/packages/31/04/ec8c1dd9250847303d98516e917978cb1c7083024770d86d657d2ccb5a70/sentry_sdk-2.42.1.tar.gz", hash = "sha256:8598cc6edcfe74cb8074ba6a7c15338cdee93d63d3eb9b9943b4b568354ad5b6", size = 354839, upload-time = "2025-10-20T12:38:40.45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/58/175d0e4d93f62075a01f8aebe904b412c34a94a4517e5045d0a1d512aad0/sentry_sdk-2.41.0-py2.py3-none-any.whl", hash = "sha256:343cde6540574113d13d178d1b2093e011ac21dd55abd3a1ec7e540f0d18a5bd", size = 370606, upload-time = "2025-10-09T14:12:19.003Z" }, + { url = "https://files.pythonhosted.org/packages/0f/cb/c21b96ff379923310b4fb2c06e8d560d801e24aeb300faa72a04776868fc/sentry_sdk-2.42.1-py2.py3-none-any.whl", hash = "sha256:f8716b50c927d3beb41bc88439dc6bcd872237b596df5b14613e2ade104aee02", size = 380952, upload-time = "2025-10-20T12:38:38.88Z" }, ] [[package]] @@ -2051,7 +2040,7 @@ wheels = [ [[package]] name = "typer" -version = "0.19.2" +version = "0.20.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -2059,9 +2048,9 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/21/ca/950278884e2ca20547ff3eb109478c6baf6b8cf219318e6bc4f666fad8e8/typer-0.19.2.tar.gz", hash = "sha256:9ad824308ded0ad06cc716434705f691d4ee0bfd0fb081839d2e426860e7fdca", size = 104755, upload-time = "2025-09-23T09:47:48.256Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8f/28/7c85c8032b91dbe79725b6f17d2fffc595dff06a35c7a30a37bef73a1ab4/typer-0.20.0.tar.gz", hash = "sha256:1aaf6494031793e4876fb0bacfa6a912b551cf43c1e63c800df8b1a866720c37", size = 106492, upload-time = "2025-10-20T17:03:49.445Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/22/35617eee79080a5d071d0f14ad698d325ee6b3bf824fc0467c03b30e7fa8/typer-0.19.2-py3-none-any.whl", hash = "sha256:755e7e19670ffad8283db353267cb81ef252f595aa6834a0d1ca9312d9326cb9", size = 46748, upload-time = "2025-09-23T09:47:46.777Z" }, + { url = "https://files.pythonhosted.org/packages/78/64/7713ffe4b5983314e9d436a90d5bd4f63b6054e2aca783a3cfc44cb95bbf/typer-0.20.0-py3-none-any.whl", hash = "sha256:5b463df6793ec1dca6213a3cf4c0f03bc6e322ac5e16e13ddd622a889489784a", size = 47028, upload-time = "2025-10-20T17:03:47.617Z" }, ] [[package]] @@ -2114,15 +2103,15 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.37.0" +version = "0.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/57/1616c8274c3442d802621abf5deb230771c7a0fec9414cb6763900eb3868/uvicorn-0.37.0.tar.gz", hash = "sha256:4115c8add6d3fd536c8ee77f0e14a7fd2ebba939fed9b02583a97f80648f9e13", size = 80367, upload-time = "2025-09-23T13:33:47.486Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605, upload-time = "2025-10-18T13:46:44.63Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/cd/584a2ceb5532af99dd09e50919e3615ba99aa127e9850eafe5f31ddfdb9a/uvicorn-0.37.0-py3-none-any.whl", hash = "sha256:913b2b88672343739927ce381ff9e2ad62541f9f8289664fa1d1d3803fa2ce6c", size = 67976, upload-time = "2025-09-23T13:33:45.842Z" }, + { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109, upload-time = "2025-10-18T13:46:42.958Z" }, ] [package.optional-dependencies] @@ -2138,16 +2127,28 @@ standard = [ [[package]] name = "uvloop" -version = "0.21.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741, upload-time = "2024-10-14T23:38:35.489Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/8d/2cbef610ca21539f0f36e2b34da49302029e7c9f09acef0b1c3b5839412b/uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281", size = 1468123, upload-time = "2024-10-14T23:38:00.688Z" }, - { url = "https://files.pythonhosted.org/packages/93/0d/b0038d5a469f94ed8f2b2fce2434a18396d8fbfb5da85a0a9781ebbdec14/uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af", size = 819325, upload-time = "2024-10-14T23:38:02.309Z" }, - { url = "https://files.pythonhosted.org/packages/50/94/0a687f39e78c4c1e02e3272c6b2ccdb4e0085fda3b8352fecd0410ccf915/uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6", size = 4582806, upload-time = "2024-10-14T23:38:04.711Z" }, - { url = "https://files.pythonhosted.org/packages/d2/19/f5b78616566ea68edd42aacaf645adbf71fbd83fc52281fba555dc27e3f1/uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816", size = 4701068, upload-time = "2024-10-14T23:38:06.385Z" }, - { url = "https://files.pythonhosted.org/packages/47/57/66f061ee118f413cd22a656de622925097170b9380b30091b78ea0c6ea75/uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc", size = 4454428, upload-time = "2024-10-14T23:38:08.416Z" }, - { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018, upload-time = "2024-10-14T23:38:10.888Z" }, +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, + { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, + { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, + { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, + { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, + { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, + { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, + { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, + { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, + { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, ] [[package]] diff --git a/frontend-app/src/assets/data/demo.json b/frontend-app/src/assets/data/demo.json deleted file mode 100644 index 3289842..0000000 --- a/frontend-app/src/assets/data/demo.json +++ /dev/null @@ -1,50791 +0,0 @@ -{ - "products": [ - { - "id": 1, - "name": "SmartToast Pro 2000", - "description": "Advanced 4-slice toaster with smart temperature control and multiple browning settings", - "brand": "SmartToast", - "model": "pr-2000-3111", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.8, - "unit": "kg" - }, - { - "property_name": "Width", - "value": 35, - "unit": "cm" - }, - { - "property_name": "Height", - "value": 22, - "unit": "cm" - }, - { - "property_name": "Depth", - "value": 18, - "unit": "cm" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 45, - "mass": 1.26, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 35, - "mass": 0.98, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 15, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 5, - "mass": 0.14, - "unit": "kg" - } - ], - "components": [ - { - "id": 101, - "name": "Heating Element", - "description": "Nichrome wire heating element" - }, - { - "id": 102, - "name": "Control Panel", - "description": "Digital display with touch controls" - }, - { - "id": 103, - "name": "Spring Mechanism", - "description": "Pop-up mechanism for toast ejection" - }, - { - "id": 104, - "name": "Crumb Tray", - "description": "Removable stainless steel crumb collection tray" - } - ], - "images": [ - { - "id": 1, - "url": "https://via.placeholder.com/400x300/0066cc/ffffff?text=SmartToast+Pro+2000", - "description": "Front view of toaster" - } - ], - "created_at": "2024-01-15T10:30:00Z", - "updated_at": "2024-06-15T14:22:00Z" - }, - { - "id": 2, - "name": "EcoBoil Electric Kettle", - "description": "Energy-efficient 1.7L electric kettle with temperature control and auto shut-off", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.2, - "unit": "kg" - }, - { - "property_name": "Capacity", - "value": 1.7, - "unit": "L" - }, - { - "property_name": "Height", - "value": 24, - "unit": "cm" - }, - { - "property_name": "Base Diameter", - "value": 20, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2200, - "unit": "W" - }, - { - "property_name": "Boiling Time", - "value": 3.5, - "unit": "min" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 60, - "mass": 0.72, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 25, - "mass": 0.3, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 10, - "mass": 0.12, - "unit": "kg" - }, - { - "material": { - "id": 8, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 5, - "mass": 0.06, - "unit": "kg" - } - ], - "components": [ - { - "id": 201, - "name": "Heating Base", - "description": "Electric heating element with power cord" - }, - { - "id": 202, - "name": "Glass Body", - "description": "Borosilicate glass water container" - }, - { - "id": 203, - "name": "Temperature Sensor", - "description": "Auto shut-off temperature control" - }, - { - "id": 204, - "name": "Handle Assembly", - "description": "Ergonomic heat-resistant handle" - }, - { - "id": 205, - "name": "Lid Mechanism", - "description": "Spring-loaded lid with steam vent" - } - ], - "images": [ - { - "id": 2, - "url": "https://via.placeholder.com/400x300/009966/ffffff?text=EcoBoil+Electric+Kettle", - "description": "Side view of electric kettle" - } - ], - "created_at": "2024-02-20T09:15:00Z", - "updated_at": "2024-06-10T11:45:00Z" - }, - { - "id": 3, - "name": "ZenGear Iron 607C", - "description": "ZenGear Iron 607C is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.62, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1332, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 56.3, - "mass": 0.83, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 43.7, - "mass": 1.48, - "unit": "kg" - } - ], - "components": [ - { - "id": 31, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 32, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 33, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 3, - "url": "https://via.placeholder.com/400x300/0da203/ffffff?text=ZenGear+Iron+607C", - "description": "Iron product image" - } - ], - "created_at": "2025-02-09T09:16:11.448062Z", - "updated_at": "2025-05-01T09:16:11.448062Z" - }, - { - "id": 4, - "name": "EcoTech Kettle 101P", - "description": "EcoTech Kettle 101P is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.61, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2176, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 38.1, - "mass": 1.08, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 39.7, - "mass": 1.18, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 22.2, - "mass": 0.23, - "unit": "kg" - } - ], - "components": [ - { - "id": 41, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 42, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 4, - "url": "https://via.placeholder.com/400x300/069a50/ffffff?text=EcoTech+Kettle+101P", - "description": "Kettle product image" - } - ], - "created_at": "2024-12-01T09:16:11.448126Z", - "updated_at": "2025-07-03T09:16:11.448126Z" - }, - { - "id": 5, - "name": "CleanWave Blender 604Z", - "description": "CleanWave Blender 604Z is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.68, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1210, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 38.6, - "mass": 1.05, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 29.9, - "mass": 0.81, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 31.5, - "mass": 0.47, - "unit": "kg" - } - ], - "components": [ - { - "id": 51, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 52, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 53, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 54, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 5, - "url": "https://via.placeholder.com/400x300/072c44/ffffff?text=CleanWave+Blender+604Z", - "description": "Blender product image" - } - ], - "created_at": "2025-04-01T09:16:11.448191Z", - "updated_at": "2025-07-19T09:16:11.448191Z" - }, - { - "id": 6, - "name": "AquaPro Monitor 996S", - "description": "AquaPro Monitor 996S is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.61, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1520, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 8.9, - "mass": 0.11, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 42.3, - "mass": 1.68, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 48.8, - "mass": 0.8, - "unit": "kg" - } - ], - "components": [ - { - "id": 61, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 62, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 6, - "url": "https://via.placeholder.com/400x300/0d7971/ffffff?text=AquaPro+Monitor+996S", - "description": "Monitor product image" - } - ], - "created_at": "2024-09-08T09:16:11.448244Z", - "updated_at": "2025-06-13T09:16:11.448244Z" - }, - { - "id": 7, - "name": "PureLife Iron 148J", - "description": "PureLife Iron 148J is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.55, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2134, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 14.3, - "mass": 0.5, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 19.3, - "mass": 0.59, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 42.9, - "mass": 1.31, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 23.5, - "mass": 0.53, - "unit": "kg" - } - ], - "components": [ - { - "id": 71, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 72, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 73, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 7, - "url": "https://via.placeholder.com/400x300/06f447/ffffff?text=PureLife+Iron+148J", - "description": "Iron product image" - } - ], - "created_at": "2024-06-17T09:16:11.448297Z", - "updated_at": "2025-05-30T09:16:11.448297Z" - }, - { - "id": 8, - "name": "SmartHome Toaster 171W", - "description": "SmartHome Toaster 171W is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.24, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 831, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 71.2, - "mass": 2.77, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 28.7, - "mass": 0.83, - "unit": "kg" - } - ], - "components": [ - { - "id": 81, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 82, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 8, - "url": "https://via.placeholder.com/400x300/0c50a7/ffffff?text=SmartHome+Toaster+171W", - "description": "Toaster product image" - } - ], - "created_at": "2024-12-19T09:16:11.448351Z", - "updated_at": "2025-06-08T09:16:11.448351Z" - }, - { - "id": 9, - "name": "ZenGear Kettle 987Y", - "description": "ZenGear Kettle 987Y is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.28, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 808, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 38.1, - "mass": 1.34, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 23.9, - "mass": 0.96, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 38.1, - "mass": 0.74, - "unit": "kg" - } - ], - "components": [ - { - "id": 91, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 92, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 93, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 94, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - }, - { - "id": 95, - "name": "Kettle Component 5", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 9, - "url": "https://via.placeholder.com/400x300/0c41dd/ffffff?text=ZenGear+Kettle+987Y", - "description": "Kettle product image" - } - ], - "created_at": "2024-12-07T09:16:11.448425Z", - "updated_at": "2025-05-27T09:16:11.448425Z" - }, - { - "id": 10, - "name": "EcoTech Rice Cooker 218T", - "description": "EcoTech Rice Cooker 218T is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.57, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1245, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 55.6, - "mass": 2.02, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 44.4, - "mass": 1.19, - "unit": "kg" - } - ], - "components": [ - { - "id": 101, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 102, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 10, - "url": "https://via.placeholder.com/400x300/0265e0/ffffff?text=EcoTech+Rice+Cooker+218T", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-09-23T09:16:11.448490Z", - "updated_at": "2025-06-30T09:16:11.448490Z" - }, - { - "id": 11, - "name": "EcoTech Coffee Maker 433D", - "description": "EcoTech Coffee Maker 433D is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.83, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1320, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 37.3, - "mass": 0.44, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 44.9, - "mass": 1.13, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 17.8, - "mass": 0.4, - "unit": "kg" - } - ], - "components": [ - { - "id": 111, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 112, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 11, - "url": "https://via.placeholder.com/400x300/0dbfa2/ffffff?text=EcoTech+Coffee+Maker+433D", - "description": "Coffee Maker product image" - } - ], - "created_at": "2025-03-12T09:16:11.448541Z", - "updated_at": "2025-04-27T09:16:11.448541Z" - }, - { - "id": 12, - "name": "CleanWave Toaster 161K", - "description": "CleanWave Toaster 161K is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.3, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1429, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 22.8, - "mass": 0.56, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 56.5, - "mass": 1.12, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 20.7, - "mass": 0.41, - "unit": "kg" - } - ], - "components": [ - { - "id": 121, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 122, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 123, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 12, - "url": "https://via.placeholder.com/400x300/02c151/ffffff?text=CleanWave+Toaster+161K", - "description": "Toaster product image" - } - ], - "created_at": "2024-05-15T09:16:11.448588Z", - "updated_at": "2025-07-14T09:16:11.448588Z" - }, - { - "id": 13, - "name": "ChefMate Monitor 267O", - "description": "ChefMate Monitor 267O is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.71, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 893, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 17.2, - "mass": 0.33, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 19.4, - "mass": 0.55, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 22.4, - "mass": 0.31, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 41.0, - "mass": 0.68, - "unit": "kg" - } - ], - "components": [ - { - "id": 131, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 132, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 13, - "url": "https://via.placeholder.com/400x300/03dc20/ffffff?text=ChefMate+Monitor+267O", - "description": "Monitor product image" - } - ], - "created_at": "2024-12-15T09:16:11.448644Z", - "updated_at": "2025-06-24T09:16:11.448644Z" - }, - { - "id": 14, - "name": "SmartHome Toaster 457E", - "description": "SmartHome Toaster 457E is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.25, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2091, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 16.7, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 83.3, - "mass": 0.99, - "unit": "kg" - } - ], - "components": [ - { - "id": 141, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 142, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 143, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 14, - "url": "https://via.placeholder.com/400x300/050341/ffffff?text=SmartHome+Toaster+457E", - "description": "Toaster product image" - } - ], - "created_at": "2024-06-19T09:16:11.448693Z", - "updated_at": "2025-07-03T09:16:11.448693Z" - }, - { - "id": 15, - "name": "SmartHome Humidifier 945H", - "description": "SmartHome Humidifier 945H is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.73, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 895, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 32.3, - "mass": 0.9, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 13.5, - "mass": 0.32, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 15.8, - "mass": 0.43, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 38.3, - "mass": 1.29, - "unit": "kg" - } - ], - "components": [ - { - "id": 151, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 152, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 153, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 15, - "url": "https://via.placeholder.com/400x300/0a70db/ffffff?text=SmartHome+Humidifier+945H", - "description": "Humidifier product image" - } - ], - "created_at": "2024-08-15T09:16:11.448751Z", - "updated_at": "2025-07-06T09:16:11.448751Z" - }, - { - "id": 16, - "name": "CleanWave Monitor 211U", - "description": "CleanWave Monitor 211U is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.35, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1361, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 20.2, - "mass": 0.45, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 26.4, - "mass": 0.95, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 35.6, - "mass": 0.44, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 17.8, - "mass": 0.25, - "unit": "kg" - } - ], - "components": [ - { - "id": 161, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 162, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 163, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 164, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 16, - "url": "https://via.placeholder.com/400x300/0294b4/ffffff?text=CleanWave+Monitor+211U", - "description": "Monitor product image" - } - ], - "created_at": "2024-06-16T09:16:11.448814Z", - "updated_at": "2025-05-02T09:16:11.448814Z" - }, - { - "id": 17, - "name": "PureLife Iron 216C", - "description": "PureLife Iron 216C is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.34, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1146, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 25.5, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 15.3, - "mass": 0.31, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 36.3, - "mass": 0.67, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 22.9, - "mass": 0.6, - "unit": "kg" - } - ], - "components": [ - { - "id": 171, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 172, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 17, - "url": "https://via.placeholder.com/400x300/0e8e27/ffffff?text=PureLife+Iron+216C", - "description": "Iron product image" - } - ], - "created_at": "2024-11-27T09:16:11.448862Z", - "updated_at": "2025-06-09T09:16:11.448862Z" - }, - { - "id": 18, - "name": "CleanWave Coffee Maker 800U", - "description": "CleanWave Coffee Maker 800U is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.19, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2100, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 29.1, - "mass": 0.61, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 22.3, - "mass": 0.6, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 48.5, - "mass": 1.77, - "unit": "kg" - } - ], - "components": [ - { - "id": 181, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 182, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 183, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 18, - "url": "https://via.placeholder.com/400x300/0a7c26/ffffff?text=CleanWave+Coffee+Maker+800U", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-09-11T09:16:11.448908Z", - "updated_at": "2025-07-13T09:16:11.448908Z" - }, - { - "id": 19, - "name": "ZenGear Rice Cooker 172F", - "description": "ZenGear Rice Cooker 172F is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.6, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2192, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 54.3, - "mass": 1.82, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 45.7, - "mass": 1.31, - "unit": "kg" - } - ], - "components": [ - { - "id": 191, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 192, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 193, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 19, - "url": "https://via.placeholder.com/400x300/036ad5/ffffff?text=ZenGear+Rice+Cooker+172F", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-11-24T09:16:11.448939Z", - "updated_at": "2025-07-08T09:16:11.448939Z" - }, - { - "id": 20, - "name": "ZenGear Iron 414Y", - "description": "ZenGear Iron 414Y is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.81, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1082, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 16.4, - "mass": 0.29, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 36.9, - "mass": 1.34, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 46.7, - "mass": 1.26, - "unit": "kg" - } - ], - "components": [ - { - "id": 201, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 202, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 20, - "url": "https://via.placeholder.com/400x300/0cd673/ffffff?text=ZenGear+Iron+414Y", - "description": "Iron product image" - } - ], - "created_at": "2025-01-06T09:16:11.448987Z", - "updated_at": "2025-05-06T09:16:11.448987Z" - }, - { - "id": 21, - "name": "PureLife Rice Cooker 449F", - "description": "PureLife Rice Cooker 449F is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.95, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2053, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 52.8, - "mass": 0.72, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 47.2, - "mass": 1.59, - "unit": "kg" - } - ], - "components": [ - { - "id": 211, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 212, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 213, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 21, - "url": "https://via.placeholder.com/400x300/04576b/ffffff?text=PureLife+Rice+Cooker+449F", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-09-10T09:16:11.449032Z", - "updated_at": "2025-05-29T09:16:11.449032Z" - }, - { - "id": 22, - "name": "AquaPro Blender 361J", - "description": "AquaPro Blender 361J is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.89, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1695, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 35.8, - "mass": 1.16, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 38.7, - "mass": 1.25, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 25.5, - "mass": 0.82, - "unit": "kg" - } - ], - "components": [ - { - "id": 221, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 222, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 223, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 224, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 22, - "url": "https://via.placeholder.com/400x300/068aab/ffffff?text=AquaPro+Blender+361J", - "description": "Blender product image" - } - ], - "created_at": "2025-03-17T09:16:11.449086Z", - "updated_at": "2025-06-28T09:16:11.449086Z" - }, - { - "id": 23, - "name": "SmartHome Kettle 600E", - "description": "SmartHome Kettle 600E is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.15, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 887, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 41.3, - "mass": 0.46, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 58.7, - "mass": 2.24, - "unit": "kg" - } - ], - "components": [ - { - "id": 231, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 232, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 233, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 234, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 23, - "url": "https://via.placeholder.com/400x300/03810c/ffffff?text=SmartHome+Kettle+600E", - "description": "Kettle product image" - } - ], - "created_at": "2024-06-24T09:16:11.449263Z", - "updated_at": "2025-06-30T09:16:11.449263Z" - }, - { - "id": 24, - "name": "AquaPro Toaster 875K", - "description": "AquaPro Toaster 875K is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.02, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2144, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 16.6, - "mass": 0.61, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 27.8, - "mass": 1.04, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 29.4, - "mass": 0.95, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 26.2, - "mass": 0.88, - "unit": "kg" - } - ], - "components": [ - { - "id": 241, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 242, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 243, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 24, - "url": "https://via.placeholder.com/400x300/0639a7/ffffff?text=AquaPro+Toaster+875K", - "description": "Toaster product image" - } - ], - "created_at": "2025-02-11T09:16:11.449344Z", - "updated_at": "2025-05-02T09:16:11.449344Z" - }, - { - "id": 25, - "name": "EcoTech Toaster 888I", - "description": "EcoTech Toaster 888I is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.71, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1052, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 31.4, - "mass": 1.24, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 54.3, - "mass": 2.13, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 14.3, - "mass": 0.38, - "unit": "kg" - } - ], - "components": [ - { - "id": 251, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 252, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 253, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 254, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - }, - { - "id": 255, - "name": "Toaster Component 5", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 25, - "url": "https://via.placeholder.com/400x300/0f345d/ffffff?text=EcoTech+Toaster+888I", - "description": "Toaster product image" - } - ], - "created_at": "2024-11-15T09:16:11.449404Z", - "updated_at": "2025-05-16T09:16:11.449404Z" - }, - { - "id": 26, - "name": "NeoCook Humidifier 796I", - "description": "NeoCook Humidifier 796I is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.58, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 824, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 10.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 70.3, - "mass": 2.54, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 29.7, - "mass": 0.86, - "unit": "kg" - } - ], - "components": [ - { - "id": 261, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 262, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 263, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 264, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 26, - "url": "https://via.placeholder.com/400x300/0c9cb6/ffffff?text=NeoCook+Humidifier+796I", - "description": "Humidifier product image" - } - ], - "created_at": "2024-04-09T09:16:11.449463Z", - "updated_at": "2025-06-23T09:16:11.449463Z" - }, - { - "id": 27, - "name": "SmartHome Rice Cooker 876N", - "description": "SmartHome Rice Cooker 876N is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.31, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2037, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 48.9, - "mass": 1.66, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 51.1, - "mass": 0.52, - "unit": "kg" - } - ], - "components": [ - { - "id": 271, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 272, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 27, - "url": "https://via.placeholder.com/400x300/0d4b5a/ffffff?text=SmartHome+Rice+Cooker+876N", - "description": "Rice Cooker product image" - } - ], - "created_at": "2025-04-15T09:16:11.449525Z", - "updated_at": "2025-06-12T09:16:11.449525Z" - }, - { - "id": 28, - "name": "PureLife Fan 661Y", - "description": "PureLife Fan 661Y is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.98, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1543, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 42.2, - "mass": 1.25, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 57.8, - "mass": 1.35, - "unit": "kg" - } - ], - "components": [ - { - "id": 281, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 282, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 28, - "url": "https://via.placeholder.com/400x300/06f71a/ffffff?text=PureLife+Fan+661Y", - "description": "Fan product image" - } - ], - "created_at": "2024-11-06T09:16:11.449580Z", - "updated_at": "2025-05-10T09:16:11.449580Z" - }, - { - "id": 29, - "name": "PureLife Kettle 359P", - "description": "PureLife Kettle 359P is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.16, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1849, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 59.2, - "mass": 1.76, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 40.8, - "mass": 1.01, - "unit": "kg" - } - ], - "components": [ - { - "id": 291, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 292, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 293, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 294, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 29, - "url": "https://via.placeholder.com/400x300/023dff/ffffff?text=PureLife+Kettle+359P", - "description": "Kettle product image" - } - ], - "created_at": "2025-02-27T09:16:11.449635Z", - "updated_at": "2025-07-11T09:16:11.449635Z" - }, - { - "id": 30, - "name": "ChefMate Humidifier 229A", - "description": "ChefMate Humidifier 229A is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.29, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1712, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 13.8, - "mass": 0.18, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 51.7, - "mass": 1.38, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 34.5, - "mass": 1.3, - "unit": "kg" - } - ], - "components": [ - { - "id": 301, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 302, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 303, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 304, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 30, - "url": "https://via.placeholder.com/400x300/03af0e/ffffff?text=ChefMate+Humidifier+229A", - "description": "Humidifier product image" - } - ], - "created_at": "2024-06-22T09:16:11.449692Z", - "updated_at": "2025-07-26T09:16:11.449692Z" - }, - { - "id": 31, - "name": "PureLife Air Purifier 405S", - "description": "PureLife Air Purifier 405S is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.85, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1108, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 22.6, - "mass": 0.24, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 24.8, - "mass": 0.69, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 43.8, - "mass": 1.15, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 8.8, - "mass": 0.19, - "unit": "kg" - } - ], - "components": [ - { - "id": 311, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 312, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 313, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 31, - "url": "https://via.placeholder.com/400x300/097300/ffffff?text=PureLife+Air+Purifier+405S", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-06-15T09:16:11.449731Z", - "updated_at": "2025-06-11T09:16:11.449731Z" - }, - { - "id": 32, - "name": "PureLife Blender 476H", - "description": "PureLife Blender 476H is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.49, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1712, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 62.1, - "mass": 1.3, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 37.9, - "mass": 0.74, - "unit": "kg" - } - ], - "components": [ - { - "id": 321, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 322, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 32, - "url": "https://via.placeholder.com/400x300/06c4a0/ffffff?text=PureLife+Blender+476H", - "description": "Blender product image" - } - ], - "created_at": "2025-01-20T09:16:11.449760Z", - "updated_at": "2025-05-28T09:16:11.449760Z" - }, - { - "id": 33, - "name": "ChefMate Iron 478S", - "description": "ChefMate Iron 478S is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.29, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1948, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 18.1, - "mass": 0.54, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 55.2, - "mass": 2.06, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 26.7, - "mass": 0.88, - "unit": "kg" - } - ], - "components": [ - { - "id": 331, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 332, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 33, - "url": "https://via.placeholder.com/400x300/0b8f9f/ffffff?text=ChefMate+Iron+478S", - "description": "Iron product image" - } - ], - "created_at": "2024-09-17T09:16:11.449791Z", - "updated_at": "2025-04-30T09:16:11.449791Z" - }, - { - "id": 34, - "name": "PureLife Coffee Maker 338V", - "description": "PureLife Coffee Maker 338V is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.25, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 944, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 41.9, - "mass": 1.08, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 58.1, - "mass": 2.0, - "unit": "kg" - } - ], - "components": [ - { - "id": 341, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 342, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 343, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 34, - "url": "https://via.placeholder.com/400x300/048f56/ffffff?text=PureLife+Coffee+Maker+338V", - "description": "Coffee Maker product image" - } - ], - "created_at": "2025-02-10T09:16:11.449820Z", - "updated_at": "2025-07-30T09:16:11.449820Z" - }, - { - "id": 35, - "name": "AquaPro Humidifier 442U", - "description": "AquaPro Humidifier 442U is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.36, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 933, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 57.7, - "mass": 2.22, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 42.3, - "mass": 1.24, - "unit": "kg" - } - ], - "components": [ - { - "id": 351, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 352, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 353, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 354, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 355, - "name": "Humidifier Component 5", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 35, - "url": "https://via.placeholder.com/400x300/0e23c6/ffffff?text=AquaPro+Humidifier+442U", - "description": "Humidifier product image" - } - ], - "created_at": "2025-03-03T09:16:11.449869Z", - "updated_at": "2025-05-23T09:16:11.449869Z" - }, - { - "id": 36, - "name": "PureLife Iron 423Q", - "description": "PureLife Iron 423Q is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.49, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 978, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 20.3, - "mass": 0.36, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 18.8, - "mass": 0.43, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 33.3, - "mass": 0.66, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 27.5, - "mass": 0.82, - "unit": "kg" - } - ], - "components": [ - { - "id": 361, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 362, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 363, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 364, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - }, - { - "id": 365, - "name": "Iron Component 5", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 36, - "url": "https://via.placeholder.com/400x300/04726a/ffffff?text=PureLife+Iron+423Q", - "description": "Iron product image" - } - ], - "created_at": "2024-03-27T09:16:11.449941Z", - "updated_at": "2025-05-18T09:16:11.449941Z" - }, - { - "id": 37, - "name": "PureLife Rice Cooker 677F", - "description": "PureLife Rice Cooker 677F is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.91, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1711, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 29.4, - "mass": 0.86, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 70.6, - "mass": 1.53, - "unit": "kg" - } - ], - "components": [ - { - "id": 371, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 372, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 373, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 37, - "url": "https://via.placeholder.com/400x300/030e25/ffffff?text=PureLife+Rice+Cooker+677F", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-06-05T09:16:11.449994Z", - "updated_at": "2025-05-25T09:16:11.449994Z" - }, - { - "id": 38, - "name": "EcoTech Monitor 581K", - "description": "EcoTech Monitor 581K is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.46, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1585, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 26.1, - "mass": 0.56, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 32.7, - "mass": 1.05, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 26.8, - "mass": 0.69, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 14.4, - "mass": 0.4, - "unit": "kg" - } - ], - "components": [ - { - "id": 381, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 382, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 383, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 384, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 38, - "url": "https://via.placeholder.com/400x300/0332f4/ffffff?text=EcoTech+Monitor+581K", - "description": "Monitor product image" - } - ], - "created_at": "2024-08-28T09:16:11.450054Z", - "updated_at": "2025-05-24T09:16:11.450054Z" - }, - { - "id": 39, - "name": "NeoCook Iron 319T", - "description": "NeoCook Iron 319T is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.4, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1786, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 78.1, - "mass": 2.58, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 21.9, - "mass": 0.66, - "unit": "kg" - } - ], - "components": [ - { - "id": 391, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 392, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 39, - "url": "https://via.placeholder.com/400x300/0774ba/ffffff?text=NeoCook+Iron+319T", - "description": "Iron product image" - } - ], - "created_at": "2025-02-19T09:16:11.450107Z", - "updated_at": "2025-07-03T09:16:11.450107Z" - }, - { - "id": 40, - "name": "EcoTech Air Purifier 835Q", - "description": "EcoTech Air Purifier 835Q is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.49, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1201, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 43.1, - "mass": 1.2, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 20.3, - "mass": 0.44, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 8.9, - "mass": 0.1, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 27.6, - "mass": 0.62, - "unit": "kg" - } - ], - "components": [ - { - "id": 401, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 402, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 40, - "url": "https://via.placeholder.com/400x300/07d9b6/ffffff?text=EcoTech+Air+Purifier+835Q", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-03-27T09:16:11.450162Z", - "updated_at": "2025-07-18T09:16:11.450162Z" - }, - { - "id": 41, - "name": "CleanWave Iron 565H", - "description": "CleanWave Iron 565H is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.73, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1409, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 20.6, - "mass": 0.37, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 44.1, - "mass": 1.71, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 35.3, - "mass": 0.61, - "unit": "kg" - } - ], - "components": [ - { - "id": 411, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 412, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 41, - "url": "https://via.placeholder.com/400x300/0ddb85/ffffff?text=CleanWave+Iron+565H", - "description": "Iron product image" - } - ], - "created_at": "2024-11-20T09:16:11.450211Z", - "updated_at": "2025-06-07T09:16:11.450211Z" - }, - { - "id": 42, - "name": "PureLife Blender 974S", - "description": "PureLife Blender 974S is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.79, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1410, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 54.1, - "mass": 0.63, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 45.9, - "mass": 0.83, - "unit": "kg" - } - ], - "components": [ - { - "id": 421, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 422, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 423, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 424, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 425, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 42, - "url": "https://via.placeholder.com/400x300/069e57/ffffff?text=PureLife+Blender+974S", - "description": "Blender product image" - } - ], - "created_at": "2024-10-27T09:16:11.450262Z", - "updated_at": "2025-05-19T09:16:11.450262Z" - }, - { - "id": 43, - "name": "PureLife Fan 944U", - "description": "PureLife Fan 944U is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.3, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1062, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 33.7, - "mass": 1.01, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 14.7, - "mass": 0.24, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 25.8, - "mass": 0.86, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 25.8, - "mass": 0.87, - "unit": "kg" - } - ], - "components": [ - { - "id": 431, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 432, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 433, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 434, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - }, - { - "id": 435, - "name": "Fan Component 5", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 43, - "url": "https://via.placeholder.com/400x300/0b9486/ffffff?text=PureLife+Fan+944U", - "description": "Fan product image" - } - ], - "created_at": "2024-06-28T09:16:11.450318Z", - "updated_at": "2025-06-19T09:16:11.450318Z" - }, - { - "id": 44, - "name": "SmartHome Monitor 264C", - "description": "SmartHome Monitor 264C is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.55, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 926, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 31.0, - "mass": 0.89, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 50.0, - "mass": 0.62, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 19.0, - "mass": 0.32, - "unit": "kg" - } - ], - "components": [ - { - "id": 441, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 442, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 443, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 444, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 445, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 44, - "url": "https://via.placeholder.com/400x300/0e4812/ffffff?text=SmartHome+Monitor+264C", - "description": "Monitor product image" - } - ], - "created_at": "2024-10-04T09:16:11.450374Z", - "updated_at": "2025-06-21T09:16:11.450374Z" - }, - { - "id": 45, - "name": "AquaPro Toaster 959U", - "description": "AquaPro Toaster 959U is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.2, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1487, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 47.3, - "mass": 0.66, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 52.7, - "mass": 0.62, - "unit": "kg" - } - ], - "components": [ - { - "id": 451, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 452, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 45, - "url": "https://via.placeholder.com/400x300/0b2990/ffffff?text=AquaPro+Toaster+959U", - "description": "Toaster product image" - } - ], - "created_at": "2024-06-07T09:16:11.450426Z", - "updated_at": "2025-06-16T09:16:11.450426Z" - }, - { - "id": 46, - "name": "NeoCook Monitor 646J", - "description": "NeoCook Monitor 646J is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.61, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1709, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 32.1, - "mass": 0.93, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 46.2, - "mass": 1.5, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 21.7, - "mass": 0.77, - "unit": "kg" - } - ], - "components": [ - { - "id": 461, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 462, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 463, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 464, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 465, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 46, - "url": "https://via.placeholder.com/400x300/08046c/ffffff?text=NeoCook+Monitor+646J", - "description": "Monitor product image" - } - ], - "created_at": "2024-04-17T09:16:11.450510Z", - "updated_at": "2025-05-11T09:16:11.450510Z" - }, - { - "id": 47, - "name": "ZenGear Fan 714T", - "description": "ZenGear Fan 714T is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.74, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 969, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 62.9, - "mass": 2.39, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 18.6, - "mass": 0.44, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 18.6, - "mass": 0.3, - "unit": "kg" - } - ], - "components": [ - { - "id": 471, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 472, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 473, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 474, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - }, - { - "id": 475, - "name": "Fan Component 5", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 47, - "url": "https://via.placeholder.com/400x300/0c6ad3/ffffff?text=ZenGear+Fan+714T", - "description": "Fan product image" - } - ], - "created_at": "2024-09-30T09:16:11.450571Z", - "updated_at": "2025-07-07T09:16:11.450571Z" - }, - { - "id": 48, - "name": "SmartHome Monitor 576C", - "description": "SmartHome Monitor 576C is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.72, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1312, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 39.5, - "mass": 0.8, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 60.5, - "mass": 2.19, - "unit": "kg" - } - ], - "components": [ - { - "id": 481, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 482, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 48, - "url": "https://via.placeholder.com/400x300/0afd76/ffffff?text=SmartHome+Monitor+576C", - "description": "Monitor product image" - } - ], - "created_at": "2024-07-24T09:16:11.450622Z", - "updated_at": "2025-04-26T09:16:11.450622Z" - }, - { - "id": 49, - "name": "EcoTech Coffee Maker 618O", - "description": "EcoTech Coffee Maker 618O is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.6, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1340, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 27.7, - "mass": 0.61, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 72.3, - "mass": 1.95, - "unit": "kg" - } - ], - "components": [ - { - "id": 491, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 492, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 49, - "url": "https://via.placeholder.com/400x300/0de4d0/ffffff?text=EcoTech+Coffee+Maker+618O", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-06-19T09:16:11.450670Z", - "updated_at": "2025-07-17T09:16:11.450670Z" - }, - { - "id": 50, - "name": "EcoTech Blender 695H", - "description": "EcoTech Blender 695H is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.71, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1391, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 10.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 25.0, - "mass": 0.97, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 29.9, - "mass": 1.08, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 15.3, - "mass": 0.16, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 29.9, - "mass": 0.98, - "unit": "kg" - } - ], - "components": [ - { - "id": 501, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 502, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 503, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 504, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 505, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 50, - "url": "https://via.placeholder.com/400x300/02b428/ffffff?text=EcoTech+Blender+695H", - "description": "Blender product image" - } - ], - "created_at": "2024-10-19T09:16:11.450731Z", - "updated_at": "2025-07-29T09:16:11.450731Z" - }, - { - "id": 51, - "name": "EcoTech Air Purifier 705E", - "description": "EcoTech Air Purifier 705E is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.2, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1461, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 34.3, - "mass": 1.04, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 55.9, - "mass": 2.08, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 9.8, - "mass": 0.16, - "unit": "kg" - } - ], - "components": [ - { - "id": 511, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 512, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 51, - "url": "https://via.placeholder.com/400x300/035657/ffffff?text=EcoTech+Air+Purifier+705E", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-12-27T09:16:11.450787Z", - "updated_at": "2025-04-27T09:16:11.450787Z" - }, - { - "id": 52, - "name": "AquaPro Iron 902H", - "description": "AquaPro Iron 902H is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.26, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2060, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 23.3, - "mass": 0.46, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 32.0, - "mass": 1.25, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 44.7, - "mass": 0.88, - "unit": "kg" - } - ], - "components": [ - { - "id": 521, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 522, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 52, - "url": "https://via.placeholder.com/400x300/0a3c5c/ffffff?text=AquaPro+Iron+902H", - "description": "Iron product image" - } - ], - "created_at": "2025-01-11T09:16:11.450851Z", - "updated_at": "2025-07-04T09:16:11.450851Z" - }, - { - "id": 53, - "name": "AquaPro Humidifier 598N", - "description": "AquaPro Humidifier 598N is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.12, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 30.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 802, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 17.9, - "mass": 0.4, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 82.1, - "mass": 3.0, - "unit": "kg" - } - ], - "components": [ - { - "id": 531, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 532, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 533, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 534, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 535, - "name": "Humidifier Component 5", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 53, - "url": "https://via.placeholder.com/400x300/04ebc3/ffffff?text=AquaPro+Humidifier+598N", - "description": "Humidifier product image" - } - ], - "created_at": "2024-04-02T09:16:11.450905Z", - "updated_at": "2025-05-15T09:16:11.450905Z" - }, - { - "id": 54, - "name": "ChefMate Blender 609N", - "description": "ChefMate Blender 609N is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.85, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1173, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 47.4, - "mass": 0.89, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 52.6, - "mass": 1.01, - "unit": "kg" - } - ], - "components": [ - { - "id": 541, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 542, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 54, - "url": "https://via.placeholder.com/400x300/0782a7/ffffff?text=ChefMate+Blender+609N", - "description": "Blender product image" - } - ], - "created_at": "2025-03-12T09:16:11.450954Z", - "updated_at": "2025-07-13T09:16:11.450954Z" - }, - { - "id": 55, - "name": "ChefMate Blender 554T", - "description": "ChefMate Blender 554T is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.43, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1816, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 25.8, - "mass": 0.38, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 42.7, - "mass": 1.49, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 31.5, - "mass": 1.24, - "unit": "kg" - } - ], - "components": [ - { - "id": 551, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 552, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 553, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 554, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 555, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 55, - "url": "https://via.placeholder.com/400x300/086604/ffffff?text=ChefMate+Blender+554T", - "description": "Blender product image" - } - ], - "created_at": "2024-11-10T09:16:11.451002Z", - "updated_at": "2025-06-29T09:16:11.451002Z" - }, - { - "id": 56, - "name": "ZenGear Coffee Maker 136V", - "description": "ZenGear Coffee Maker 136V is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.0, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2168, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 33.0, - "mass": 1.23, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 12.2, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 25.2, - "mass": 0.79, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 29.6, - "mass": 1.0, - "unit": "kg" - } - ], - "components": [ - { - "id": 561, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 562, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 563, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 56, - "url": "https://via.placeholder.com/400x300/0e065e/ffffff?text=ZenGear+Coffee+Maker+136V", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-05-03T09:16:11.451059Z", - "updated_at": "2025-05-24T09:16:11.451059Z" - }, - { - "id": 57, - "name": "ZenGear Air Purifier 922A", - "description": "ZenGear Air Purifier 922A is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.57, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1760, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 35.7, - "mass": 0.53, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 31.7, - "mass": 0.9, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 32.5, - "mass": 0.39, - "unit": "kg" - } - ], - "components": [ - { - "id": 571, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 572, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 573, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 57, - "url": "https://via.placeholder.com/400x300/03e73b/ffffff?text=ZenGear+Air+Purifier+922A", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-10-28T09:16:11.451110Z", - "updated_at": "2025-06-19T09:16:11.451110Z" - }, - { - "id": 58, - "name": "AquaPro Blender 481C", - "description": "AquaPro Blender 481C is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.11, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1054, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 55.8, - "mass": 0.83, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 44.2, - "mass": 0.83, - "unit": "kg" - } - ], - "components": [ - { - "id": 581, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 582, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 583, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 58, - "url": "https://via.placeholder.com/400x300/0442d3/ffffff?text=AquaPro+Blender+481C", - "description": "Blender product image" - } - ], - "created_at": "2024-08-24T09:16:11.451244Z", - "updated_at": "2025-07-11T09:16:11.451244Z" - }, - { - "id": 59, - "name": "NeoCook Blender 846D", - "description": "NeoCook Blender 846D is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.69, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2176, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 42.1, - "mass": 0.88, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 57.9, - "mass": 1.87, - "unit": "kg" - } - ], - "components": [ - { - "id": 591, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 592, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 593, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 594, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 59, - "url": "https://via.placeholder.com/400x300/0d36a8/ffffff?text=NeoCook+Blender+846D", - "description": "Blender product image" - } - ], - "created_at": "2025-04-19T09:16:11.451291Z", - "updated_at": "2025-07-25T09:16:11.451291Z" - }, - { - "id": 60, - "name": "SmartHome Rice Cooker 607S", - "description": "SmartHome Rice Cooker 607S is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.68, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1531, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 42.2, - "mass": 0.92, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 57.8, - "mass": 1.41, - "unit": "kg" - } - ], - "components": [ - { - "id": 601, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 602, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 603, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 60, - "url": "https://via.placeholder.com/400x300/05bcce/ffffff?text=SmartHome+Rice+Cooker+607S", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-05-04T09:16:11.451331Z", - "updated_at": "2025-06-02T09:16:11.451331Z" - }, - { - "id": 61, - "name": "AquaPro Fan 781X", - "description": "AquaPro Fan 781X is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.95, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1849, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 29.0, - "mass": 0.87, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 8.9, - "mass": 0.25, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 21.8, - "mass": 0.69, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 40.3, - "mass": 0.96, - "unit": "kg" - } - ], - "components": [ - { - "id": 611, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 612, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 613, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 614, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - }, - { - "id": 615, - "name": "Fan Component 5", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 61, - "url": "https://via.placeholder.com/400x300/04115f/ffffff?text=AquaPro+Fan+781X", - "description": "Fan product image" - } - ], - "created_at": "2024-10-25T09:16:11.451377Z", - "updated_at": "2025-06-24T09:16:11.451377Z" - }, - { - "id": 62, - "name": "NeoCook Humidifier 532S", - "description": "NeoCook Humidifier 532S is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.34, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2171, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 27.2, - "mass": 0.9, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 28.9, - "mass": 1.03, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 10.5, - "mass": 0.39, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 33.3, - "mass": 0.82, - "unit": "kg" - } - ], - "components": [ - { - "id": 621, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 622, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 62, - "url": "https://via.placeholder.com/400x300/0d16cf/ffffff?text=NeoCook+Humidifier+532S", - "description": "Humidifier product image" - } - ], - "created_at": "2024-10-19T09:16:11.451428Z", - "updated_at": "2025-06-18T09:16:11.451428Z" - }, - { - "id": 63, - "name": "CleanWave Toaster 934J", - "description": "CleanWave Toaster 934J is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.09, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2044, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 30.9, - "mass": 1.05, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 37.5, - "mass": 1.04, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 31.6, - "mass": 0.35, - "unit": "kg" - } - ], - "components": [ - { - "id": 631, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 632, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 633, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 63, - "url": "https://via.placeholder.com/400x300/0b850a/ffffff?text=CleanWave+Toaster+934J", - "description": "Toaster product image" - } - ], - "created_at": "2024-10-12T09:16:11.451483Z", - "updated_at": "2025-07-17T09:16:11.451483Z" - }, - { - "id": 64, - "name": "ChefMate Humidifier 817V", - "description": "ChefMate Humidifier 817V is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.85, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 931, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 24.6, - "mass": 0.38, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 16.6, - "mass": 0.39, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 32.6, - "mass": 0.83, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 26.3, - "mass": 0.62, - "unit": "kg" - } - ], - "components": [ - { - "id": 641, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 642, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 643, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 644, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 645, - "name": "Humidifier Component 5", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 64, - "url": "https://via.placeholder.com/400x300/0c871e/ffffff?text=ChefMate+Humidifier+817V", - "description": "Humidifier product image" - } - ], - "created_at": "2024-06-14T09:16:11.451537Z", - "updated_at": "2025-05-23T09:16:11.451537Z" - }, - { - "id": 65, - "name": "EcoTech Monitor 888W", - "description": "EcoTech Monitor 888W is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.73, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1091, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 36.6, - "mass": 0.65, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 42.3, - "mass": 1.41, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 21.1, - "mass": 0.56, - "unit": "kg" - } - ], - "components": [ - { - "id": 651, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 652, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 653, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 654, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 65, - "url": "https://via.placeholder.com/400x300/0a8e4d/ffffff?text=EcoTech+Monitor+888W", - "description": "Monitor product image" - } - ], - "created_at": "2024-07-15T09:16:11.451569Z", - "updated_at": "2025-05-29T09:16:11.451569Z" - }, - { - "id": 66, - "name": "NeoCook Kettle 219G", - "description": "NeoCook Kettle 219G is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.49, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 968, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 14.4, - "mass": 0.18, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 43.9, - "mass": 1.05, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 41.7, - "mass": 1.34, - "unit": "kg" - } - ], - "components": [ - { - "id": 661, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 662, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 66, - "url": "https://via.placeholder.com/400x300/038125/ffffff?text=NeoCook+Kettle+219G", - "description": "Kettle product image" - } - ], - "created_at": "2024-03-30T09:16:11.451600Z", - "updated_at": "2025-06-26T09:16:11.451600Z" - }, - { - "id": 67, - "name": "CleanWave Fan 871L", - "description": "CleanWave Fan 871L is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.24, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1259, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 24.1, - "mass": 0.93, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 54.4, - "mass": 1.11, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 21.5, - "mass": 0.52, - "unit": "kg" - } - ], - "components": [ - { - "id": 671, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 672, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 673, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 674, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 67, - "url": "https://via.placeholder.com/400x300/06766b/ffffff?text=CleanWave+Fan+871L", - "description": "Fan product image" - } - ], - "created_at": "2024-11-30T09:16:11.451635Z", - "updated_at": "2025-04-26T09:16:11.451635Z" - }, - { - "id": 68, - "name": "EcoTech Fan 746H", - "description": "EcoTech Fan 746H is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.64, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1488, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 33.0, - "mass": 0.79, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 46.8, - "mass": 0.57, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 20.2, - "mass": 0.31, - "unit": "kg" - } - ], - "components": [ - { - "id": 681, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 682, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 683, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 684, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 68, - "url": "https://via.placeholder.com/400x300/0e6f50/ffffff?text=EcoTech+Fan+746H", - "description": "Fan product image" - } - ], - "created_at": "2025-04-05T09:16:11.451671Z", - "updated_at": "2025-05-15T09:16:11.451671Z" - }, - { - "id": 69, - "name": "NeoCook Toaster 779R", - "description": "NeoCook Toaster 779R is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.29, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2135, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 22.7, - "mass": 0.54, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 77.3, - "mass": 2.93, - "unit": "kg" - } - ], - "components": [ - { - "id": 691, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 692, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 693, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 694, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 69, - "url": "https://via.placeholder.com/400x300/05ac9b/ffffff?text=NeoCook+Toaster+779R", - "description": "Toaster product image" - } - ], - "created_at": "2025-01-25T09:16:11.451718Z", - "updated_at": "2025-06-05T09:16:11.451718Z" - }, - { - "id": 70, - "name": "NeoCook Toaster 749F", - "description": "NeoCook Toaster 749F is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.0, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 845, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 81.5, - "mass": 2.22, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 18.5, - "mass": 0.32, - "unit": "kg" - } - ], - "components": [ - { - "id": 701, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 702, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 703, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 704, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 70, - "url": "https://via.placeholder.com/400x300/0d3980/ffffff?text=NeoCook+Toaster+749F", - "description": "Toaster product image" - } - ], - "created_at": "2024-07-29T09:16:11.451770Z", - "updated_at": "2025-06-21T09:16:11.451770Z" - }, - { - "id": 71, - "name": "ZenGear Blender 125N", - "description": "ZenGear Blender 125N is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.81, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 928, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 48.6, - "mass": 1.92, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 51.4, - "mass": 1.5, - "unit": "kg" - } - ], - "components": [ - { - "id": 711, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 712, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 71, - "url": "https://via.placeholder.com/400x300/0b18a9/ffffff?text=ZenGear+Blender+125N", - "description": "Blender product image" - } - ], - "created_at": "2024-03-28T09:16:11.451819Z", - "updated_at": "2025-05-05T09:16:11.451819Z" - }, - { - "id": 72, - "name": "PureLife Toaster 874Q", - "description": "PureLife Toaster 874Q is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.13, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1878, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 35.0, - "mass": 0.85, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 31.5, - "mass": 0.61, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 33.6, - "mass": 0.83, - "unit": "kg" - } - ], - "components": [ - { - "id": 721, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 722, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 723, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 724, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 72, - "url": "https://via.placeholder.com/400x300/0f0e0d/ffffff?text=PureLife+Toaster+874Q", - "description": "Toaster product image" - } - ], - "created_at": "2025-04-05T09:16:11.451880Z", - "updated_at": "2025-06-14T09:16:11.451880Z" - }, - { - "id": 73, - "name": "ChefMate Air Purifier 590Y", - "description": "ChefMate Air Purifier 590Y is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.26, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1519, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 65.8, - "mass": 2.01, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 34.2, - "mass": 0.5, - "unit": "kg" - } - ], - "components": [ - { - "id": 731, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 732, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 733, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 73, - "url": "https://via.placeholder.com/400x300/0cc406/ffffff?text=ChefMate+Air+Purifier+590Y", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-03-30T09:16:11.451926Z", - "updated_at": "2025-06-15T09:16:11.451926Z" - }, - { - "id": 74, - "name": "AquaPro Iron 454C", - "description": "AquaPro Iron 454C is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.07, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1280, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 29.7, - "mass": 0.33, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 48.6, - "mass": 1.85, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 21.6, - "mass": 0.26, - "unit": "kg" - } - ], - "components": [ - { - "id": 741, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 742, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 743, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 744, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - }, - { - "id": 745, - "name": "Iron Component 5", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 74, - "url": "https://via.placeholder.com/400x300/07dbf9/ffffff?text=AquaPro+Iron+454C", - "description": "Iron product image" - } - ], - "created_at": "2025-03-22T09:16:11.451961Z", - "updated_at": "2025-04-27T09:16:11.451961Z" - }, - { - "id": 75, - "name": "PureLife Air Purifier 527P", - "description": "PureLife Air Purifier 527P is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.37, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2127, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 12.7, - "mass": 0.15, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 37.3, - "mass": 0.69, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 12.0, - "mass": 0.32, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 38.0, - "mass": 1.42, - "unit": "kg" - } - ], - "components": [ - { - "id": 751, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 752, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 753, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 754, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 755, - "name": "Air Purifier Component 5", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 75, - "url": "https://via.placeholder.com/400x300/043674/ffffff?text=PureLife+Air+Purifier+527P", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-05-02T09:16:11.451999Z", - "updated_at": "2025-07-24T09:16:11.451999Z" - }, - { - "id": 76, - "name": "ZenGear Monitor 938X", - "description": "ZenGear Monitor 938X is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.06, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1145, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 19.8, - "mass": 0.41, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 31.1, - "mass": 1.0, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 49.1, - "mass": 0.85, - "unit": "kg" - } - ], - "components": [ - { - "id": 761, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 762, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 763, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 76, - "url": "https://via.placeholder.com/400x300/0ddb03/ffffff?text=ZenGear+Monitor+938X", - "description": "Monitor product image" - } - ], - "created_at": "2025-02-15T09:16:11.452045Z", - "updated_at": "2025-07-11T09:16:11.452045Z" - }, - { - "id": 77, - "name": "EcoTech Monitor 598B", - "description": "EcoTech Monitor 598B is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.08, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1944, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 26.4, - "mass": 0.96, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 31.9, - "mass": 0.94, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 16.0, - "mass": 0.63, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 25.8, - "mass": 0.53, - "unit": "kg" - } - ], - "components": [ - { - "id": 771, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 772, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 773, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 774, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 775, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 77, - "url": "https://via.placeholder.com/400x300/03abc0/ffffff?text=EcoTech+Monitor+598B", - "description": "Monitor product image" - } - ], - "created_at": "2025-01-12T09:16:11.452091Z", - "updated_at": "2025-06-13T09:16:11.452091Z" - }, - { - "id": 78, - "name": "NeoCook Toaster 499E", - "description": "NeoCook Toaster 499E is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.27, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1203, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 43.8, - "mass": 1.63, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 56.2, - "mass": 1.25, - "unit": "kg" - } - ], - "components": [ - { - "id": 781, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 782, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 78, - "url": "https://via.placeholder.com/400x300/06fcae/ffffff?text=NeoCook+Toaster+499E", - "description": "Toaster product image" - } - ], - "created_at": "2025-01-13T09:16:11.452135Z", - "updated_at": "2025-07-23T09:16:11.452135Z" - }, - { - "id": 79, - "name": "PureLife Iron 780K", - "description": "PureLife Iron 780K is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.13, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1041, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 50.5, - "mass": 1.27, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 49.5, - "mass": 1.24, - "unit": "kg" - } - ], - "components": [ - { - "id": 791, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 792, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 79, - "url": "https://via.placeholder.com/400x300/0cbe03/ffffff?text=PureLife+Iron+780K", - "description": "Iron product image" - } - ], - "created_at": "2025-01-08T09:16:11.452166Z", - "updated_at": "2025-07-07T09:16:11.452166Z" - }, - { - "id": 80, - "name": "EcoTech Humidifier 864C", - "description": "EcoTech Humidifier 864C is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.08, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2052, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 68.5, - "mass": 2.73, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 31.5, - "mass": 0.81, - "unit": "kg" - } - ], - "components": [ - { - "id": 801, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 802, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 803, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 804, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 80, - "url": "https://via.placeholder.com/400x300/0454a1/ffffff?text=EcoTech+Humidifier+864C", - "description": "Humidifier product image" - } - ], - "created_at": "2024-07-17T09:16:11.452198Z", - "updated_at": "2025-06-09T09:16:11.452198Z" - }, - { - "id": 81, - "name": "SmartHome Fan 980L", - "description": "SmartHome Fan 980L is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.8, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1134, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 24.6, - "mass": 0.82, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 75.4, - "mass": 0.91, - "unit": "kg" - } - ], - "components": [ - { - "id": 811, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 812, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 813, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 814, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 81, - "url": "https://via.placeholder.com/400x300/063607/ffffff?text=SmartHome+Fan+980L", - "description": "Fan product image" - } - ], - "created_at": "2024-06-26T09:16:11.452227Z", - "updated_at": "2025-05-18T09:16:11.452227Z" - }, - { - "id": 82, - "name": "ZenGear Kettle 816G", - "description": "ZenGear Kettle 816G is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.12, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 977, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 23.4, - "mass": 0.69, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 76.6, - "mass": 1.98, - "unit": "kg" - } - ], - "components": [ - { - "id": 821, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 822, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 823, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 824, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - }, - { - "id": 825, - "name": "Kettle Component 5", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 82, - "url": "https://via.placeholder.com/400x300/03f661/ffffff?text=ZenGear+Kettle+816G", - "description": "Kettle product image" - } - ], - "created_at": "2025-04-03T09:16:11.452256Z", - "updated_at": "2025-05-22T09:16:11.452256Z" - }, - { - "id": 83, - "name": "CleanWave Kettle 743T", - "description": "CleanWave Kettle 743T is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.86, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 882, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 20.2, - "mass": 0.49, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 48.6, - "mass": 1.49, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 31.2, - "mass": 0.56, - "unit": "kg" - } - ], - "components": [ - { - "id": 831, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 832, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 833, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 834, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 83, - "url": "https://via.placeholder.com/400x300/060138/ffffff?text=CleanWave+Kettle+743T", - "description": "Kettle product image" - } - ], - "created_at": "2024-12-30T09:16:11.452541Z", - "updated_at": "2025-07-22T09:16:11.452541Z" - }, - { - "id": 84, - "name": "AquaPro Iron 426H", - "description": "AquaPro Iron 426H is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.12, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1477, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 18.2, - "mass": 0.71, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 81.8, - "mass": 2.84, - "unit": "kg" - } - ], - "components": [ - { - "id": 841, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 842, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 843, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 84, - "url": "https://via.placeholder.com/400x300/020ecf/ffffff?text=AquaPro+Iron+426H", - "description": "Iron product image" - } - ], - "created_at": "2024-08-14T09:16:11.452584Z", - "updated_at": "2025-07-15T09:16:11.452584Z" - }, - { - "id": 85, - "name": "EcoTech Humidifier 407F", - "description": "EcoTech Humidifier 407F is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.92, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1692, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 48.6, - "mass": 0.52, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 51.4, - "mass": 0.6, - "unit": "kg" - } - ], - "components": [ - { - "id": 851, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 852, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 853, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 854, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 855, - "name": "Humidifier Component 5", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 85, - "url": "https://via.placeholder.com/400x300/05ddbf/ffffff?text=EcoTech+Humidifier+407F", - "description": "Humidifier product image" - } - ], - "created_at": "2025-02-25T09:16:11.452617Z", - "updated_at": "2025-06-10T09:16:11.452617Z" - }, - { - "id": 86, - "name": "ZenGear Toaster 287I", - "description": "ZenGear Toaster 287I is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.77, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1830, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 60.6, - "mass": 2.2, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 39.4, - "mass": 0.66, - "unit": "kg" - } - ], - "components": [ - { - "id": 861, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 862, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 86, - "url": "https://via.placeholder.com/400x300/04ea76/ffffff?text=ZenGear+Toaster+287I", - "description": "Toaster product image" - } - ], - "created_at": "2024-10-24T09:16:11.452644Z", - "updated_at": "2025-07-26T09:16:11.452644Z" - }, - { - "id": 87, - "name": "EcoTech Monitor 837D", - "description": "EcoTech Monitor 837D is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.08, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1779, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 58.3, - "mass": 1.99, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 41.7, - "mass": 1.37, - "unit": "kg" - } - ], - "components": [ - { - "id": 871, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 872, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 873, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 874, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 87, - "url": "https://via.placeholder.com/400x300/0dc27f/ffffff?text=EcoTech+Monitor+837D", - "description": "Monitor product image" - } - ], - "created_at": "2024-05-28T09:16:11.452675Z", - "updated_at": "2025-06-24T09:16:11.452675Z" - }, - { - "id": 88, - "name": "PureLife Iron 727S", - "description": "PureLife Iron 727S is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.5, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1529, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 37.3, - "mass": 1.16, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 13.6, - "mass": 0.33, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 18.2, - "mass": 0.73, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 30.9, - "mass": 0.54, - "unit": "kg" - } - ], - "components": [ - { - "id": 881, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 882, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 88, - "url": "https://via.placeholder.com/400x300/0aaeaf/ffffff?text=PureLife+Iron+727S", - "description": "Iron product image" - } - ], - "created_at": "2025-03-04T09:16:11.452707Z", - "updated_at": "2025-06-22T09:16:11.452707Z" - }, - { - "id": 89, - "name": "PureLife Monitor 709R", - "description": "PureLife Monitor 709R is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.18, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1418, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 19.4, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 40.3, - "mass": 0.81, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 40.3, - "mass": 0.55, - "unit": "kg" - } - ], - "components": [ - { - "id": 891, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 892, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 893, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 894, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 895, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 89, - "url": "https://via.placeholder.com/400x300/054ec8/ffffff?text=PureLife+Monitor+709R", - "description": "Monitor product image" - } - ], - "created_at": "2024-10-20T09:16:11.452738Z", - "updated_at": "2025-06-27T09:16:11.452738Z" - }, - { - "id": 90, - "name": "SmartHome Kettle 786V", - "description": "SmartHome Kettle 786V is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.67, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1169, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 10.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 32.2, - "mass": 1.25, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 32.2, - "mass": 0.94, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 13.5, - "mass": 0.53, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 22.2, - "mass": 0.83, - "unit": "kg" - } - ], - "components": [ - { - "id": 901, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 902, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 903, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 904, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - }, - { - "id": 905, - "name": "Kettle Component 5", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 90, - "url": "https://via.placeholder.com/400x300/0a3e25/ffffff?text=SmartHome+Kettle+786V", - "description": "Kettle product image" - } - ], - "created_at": "2025-01-08T09:16:11.452771Z", - "updated_at": "2025-06-16T09:16:11.452771Z" - }, - { - "id": 91, - "name": "ChefMate Fan 942H", - "description": "ChefMate Fan 942H is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.46, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1299, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 20.8, - "mass": 0.3, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 79.2, - "mass": 2.1, - "unit": "kg" - } - ], - "components": [ - { - "id": 911, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 912, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 913, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 91, - "url": "https://via.placeholder.com/400x300/0c2c51/ffffff?text=ChefMate+Fan+942H", - "description": "Fan product image" - } - ], - "created_at": "2024-11-15T09:16:11.452799Z", - "updated_at": "2025-06-14T09:16:11.452799Z" - }, - { - "id": 92, - "name": "NeoCook Coffee Maker 473K", - "description": "NeoCook Coffee Maker 473K is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.71, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1991, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 24.7, - "mass": 0.91, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 25.3, - "mass": 0.68, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 28.2, - "mass": 0.92, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 21.8, - "mass": 0.41, - "unit": "kg" - } - ], - "components": [ - { - "id": 921, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 922, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 92, - "url": "https://via.placeholder.com/400x300/028c87/ffffff?text=NeoCook+Coffee+Maker+473K", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-03-26T09:16:11.452845Z", - "updated_at": "2025-07-08T09:16:11.452845Z" - }, - { - "id": 93, - "name": "ChefMate Fan 200G", - "description": "ChefMate Fan 200G is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.01, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1998, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 40.5, - "mass": 0.47, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 36.2, - "mass": 0.91, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 23.3, - "mass": 0.51, - "unit": "kg" - } - ], - "components": [ - { - "id": 931, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 932, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 93, - "url": "https://via.placeholder.com/400x300/0b4781/ffffff?text=ChefMate+Fan+200G", - "description": "Fan product image" - } - ], - "created_at": "2025-02-11T09:16:11.452979Z", - "updated_at": "2025-06-17T09:16:11.452979Z" - }, - { - "id": 94, - "name": "CleanWave Coffee Maker 693H", - "description": "CleanWave Coffee Maker 693H is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.39, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1061, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 52.2, - "mass": 1.01, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 30.4, - "mass": 0.37, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 17.4, - "mass": 0.64, - "unit": "kg" - } - ], - "components": [ - { - "id": 941, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 942, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 94, - "url": "https://via.placeholder.com/400x300/078609/ffffff?text=CleanWave+Coffee+Maker+693H", - "description": "Coffee Maker product image" - } - ], - "created_at": "2025-01-16T09:16:11.453014Z", - "updated_at": "2025-05-26T09:16:11.453014Z" - }, - { - "id": 95, - "name": "CleanWave Air Purifier 565A", - "description": "CleanWave Air Purifier 565A is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.49, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 30.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1724, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 34.5, - "mass": 1.37, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 36.6, - "mass": 0.51, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 28.9, - "mass": 0.73, - "unit": "kg" - } - ], - "components": [ - { - "id": 951, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 952, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 953, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 954, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 95, - "url": "https://via.placeholder.com/400x300/0488b4/ffffff?text=CleanWave+Air+Purifier+565A", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-07-09T09:16:11.453047Z", - "updated_at": "2025-07-17T09:16:11.453047Z" - }, - { - "id": 96, - "name": "ZenGear Monitor 993I", - "description": "ZenGear Monitor 993I is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.0, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1664, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 20.9, - "mass": 0.71, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 15.1, - "mass": 0.6, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 39.6, - "mass": 1.14, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 24.5, - "mass": 0.48, - "unit": "kg" - } - ], - "components": [ - { - "id": 961, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 962, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 963, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 96, - "url": "https://via.placeholder.com/400x300/0ac1a9/ffffff?text=ZenGear+Monitor+993I", - "description": "Monitor product image" - } - ], - "created_at": "2024-08-31T09:16:11.453082Z", - "updated_at": "2025-07-07T09:16:11.453082Z" - }, - { - "id": 97, - "name": "SmartHome Humidifier 193Z", - "description": "SmartHome Humidifier 193Z is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.31, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1245, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 40.4, - "mass": 0.88, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 30.8, - "mass": 0.37, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 13.0, - "mass": 0.46, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 15.8, - "mass": 0.48, - "unit": "kg" - } - ], - "components": [ - { - "id": 971, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 972, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 97, - "url": "https://via.placeholder.com/400x300/0a16c7/ffffff?text=SmartHome+Humidifier+193Z", - "description": "Humidifier product image" - } - ], - "created_at": "2025-02-10T09:16:11.453132Z", - "updated_at": "2025-06-12T09:16:11.453132Z" - }, - { - "id": 98, - "name": "AquaPro Coffee Maker 128R", - "description": "AquaPro Coffee Maker 128R is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.08, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2131, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 53.4, - "mass": 0.69, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 27.6, - "mass": 1.08, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 19.0, - "mass": 0.56, - "unit": "kg" - } - ], - "components": [ - { - "id": 981, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 982, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 983, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 98, - "url": "https://via.placeholder.com/400x300/05e3b0/ffffff?text=AquaPro+Coffee+Maker+128R", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-10-13T09:16:11.453186Z", - "updated_at": "2025-05-27T09:16:11.453186Z" - }, - { - "id": 99, - "name": "ChefMate Kettle 864I", - "description": "ChefMate Kettle 864I is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.52, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1265, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 20.0, - "mass": 0.67, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 38.4, - "mass": 1.25, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 41.6, - "mass": 1.24, - "unit": "kg" - } - ], - "components": [ - { - "id": 991, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 992, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 993, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 994, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 99, - "url": "https://via.placeholder.com/400x300/022bcc/ffffff?text=ChefMate+Kettle+864I", - "description": "Kettle product image" - } - ], - "created_at": "2024-11-09T09:16:11.453220Z", - "updated_at": "2025-06-20T09:16:11.453220Z" - }, - { - "id": 100, - "name": "CleanWave Humidifier 149T", - "description": "CleanWave Humidifier 149T is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.11, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1658, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 13.7, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 35.9, - "mass": 1.05, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 40.5, - "mass": 1.42, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 9.9, - "mass": 0.24, - "unit": "kg" - } - ], - "components": [ - { - "id": 1001, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1002, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1003, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1004, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1005, - "name": "Humidifier Component 5", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 100, - "url": "https://via.placeholder.com/400x300/0a7255/ffffff?text=CleanWave+Humidifier+149T", - "description": "Humidifier product image" - } - ], - "created_at": "2024-08-31T09:16:11.453256Z", - "updated_at": "2025-07-04T09:16:11.453256Z" - }, - { - "id": 101, - "name": "PureLife Blender 932O", - "description": "PureLife Blender 932O is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.78, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2114, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 22.0, - "mass": 0.28, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 78.0, - "mass": 1.21, - "unit": "kg" - } - ], - "components": [ - { - "id": 1011, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 1012, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 1013, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 101, - "url": "https://via.placeholder.com/400x300/0876c7/ffffff?text=PureLife+Blender+932O", - "description": "Blender product image" - } - ], - "created_at": "2024-06-09T09:16:11.453285Z", - "updated_at": "2025-06-21T09:16:11.453285Z" - }, - { - "id": 102, - "name": "EcoTech Coffee Maker 859H", - "description": "EcoTech Coffee Maker 859H is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.0, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1521, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 28.7, - "mass": 1.07, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 71.2, - "mass": 1.37, - "unit": "kg" - } - ], - "components": [ - { - "id": 1021, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1022, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1023, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 102, - "url": "https://via.placeholder.com/400x300/0495ec/ffffff?text=EcoTech+Coffee+Maker+859H", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-12-03T09:16:11.453315Z", - "updated_at": "2025-06-14T09:16:11.453315Z" - }, - { - "id": 103, - "name": "NeoCook Coffee Maker 171A", - "description": "NeoCook Coffee Maker 171A is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.03, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1065, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 18.5, - "mass": 0.53, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 23.6, - "mass": 0.45, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 29.8, - "mass": 1.01, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 28.1, - "mass": 0.42, - "unit": "kg" - } - ], - "components": [ - { - "id": 1031, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1032, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1033, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1034, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1035, - "name": "Coffee Maker Component 5", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 103, - "url": "https://via.placeholder.com/400x300/0c248e/ffffff?text=NeoCook+Coffee+Maker+171A", - "description": "Coffee Maker product image" - } - ], - "created_at": "2025-02-18T09:16:11.453348Z", - "updated_at": "2025-06-26T09:16:11.453348Z" - }, - { - "id": 104, - "name": "SmartHome Rice Cooker 419U", - "description": "SmartHome Rice Cooker 419U is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.33, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1217, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 11.9, - "mass": 0.13, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 24.6, - "mass": 0.59, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 41.0, - "mass": 1.55, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 22.4, - "mass": 0.51, - "unit": "kg" - } - ], - "components": [ - { - "id": 1041, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1042, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1043, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1044, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1045, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 104, - "url": "https://via.placeholder.com/400x300/0899aa/ffffff?text=SmartHome+Rice+Cooker+419U", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-12-14T09:16:11.453381Z", - "updated_at": "2025-05-01T09:16:11.453381Z" - }, - { - "id": 105, - "name": "AquaPro Air Purifier 306F", - "description": "AquaPro Air Purifier 306F is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.05, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1472, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 18.4, - "mass": 0.23, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 16.1, - "mass": 0.62, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 36.8, - "mass": 1.43, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 28.7, - "mass": 0.72, - "unit": "kg" - } - ], - "components": [ - { - "id": 1051, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 1052, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 1053, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 105, - "url": "https://via.placeholder.com/400x300/0eaa70/ffffff?text=AquaPro+Air+Purifier+306F", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-10-30T09:16:11.453413Z", - "updated_at": "2025-07-09T09:16:11.453413Z" - }, - { - "id": 106, - "name": "PureLife Iron 428F", - "description": "PureLife Iron 428F is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.03, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1605, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 75.0, - "mass": 1.13, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 25.0, - "mass": 0.7, - "unit": "kg" - } - ], - "components": [ - { - "id": 1061, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 1062, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 106, - "url": "https://via.placeholder.com/400x300/070382/ffffff?text=PureLife+Iron+428F", - "description": "Iron product image" - } - ], - "created_at": "2025-04-13T09:16:11.453442Z", - "updated_at": "2025-06-16T09:16:11.453442Z" - }, - { - "id": 107, - "name": "SmartHome Rice Cooker 782J", - "description": "SmartHome Rice Cooker 782J is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.75, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2091, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 33.3, - "mass": 0.79, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 46.7, - "mass": 0.5, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 20.0, - "mass": 0.38, - "unit": "kg" - } - ], - "components": [ - { - "id": 1071, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1072, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1073, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1074, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 107, - "url": "https://via.placeholder.com/400x300/0676cc/ffffff?text=SmartHome+Rice+Cooker+782J", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-05-27T09:16:11.453488Z", - "updated_at": "2025-06-09T09:16:11.453488Z" - }, - { - "id": 108, - "name": "ZenGear Blender 351S", - "description": "ZenGear Blender 351S is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.12, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1460, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 35.0, - "mass": 1.22, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 18.3, - "mass": 0.71, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 11.7, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 35.0, - "mass": 0.97, - "unit": "kg" - } - ], - "components": [ - { - "id": 1081, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 1082, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 108, - "url": "https://via.placeholder.com/400x300/0b229f/ffffff?text=ZenGear+Blender+351S", - "description": "Blender product image" - } - ], - "created_at": "2024-12-18T09:16:11.453527Z", - "updated_at": "2025-07-14T09:16:11.453527Z" - }, - { - "id": 109, - "name": "ZenGear Rice Cooker 350F", - "description": "ZenGear Rice Cooker 350F is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.94, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 950, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 51.5, - "mass": 1.8, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 48.5, - "mass": 1.33, - "unit": "kg" - } - ], - "components": [ - { - "id": 1091, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1092, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1093, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1094, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1095, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 109, - "url": "https://via.placeholder.com/400x300/0312f6/ffffff?text=ZenGear+Rice+Cooker+350F", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-09-26T09:16:11.453560Z", - "updated_at": "2025-05-02T09:16:11.453560Z" - }, - { - "id": 110, - "name": "NeoCook Blender 666A", - "description": "NeoCook Blender 666A is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.89, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1205, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 34.1, - "mass": 0.46, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 65.9, - "mass": 1.82, - "unit": "kg" - } - ], - "components": [ - { - "id": 1101, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 1102, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 1103, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 1104, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 1105, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 110, - "url": "https://via.placeholder.com/400x300/0204bc/ffffff?text=NeoCook+Blender+666A", - "description": "Blender product image" - } - ], - "created_at": "2024-11-25T09:16:11.453592Z", - "updated_at": "2025-05-28T09:16:11.453592Z" - }, - { - "id": 111, - "name": "PureLife Rice Cooker 176O", - "description": "PureLife Rice Cooker 176O is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.05, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1244, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 19.6, - "mass": 0.6, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 32.9, - "mass": 0.67, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 17.5, - "mass": 0.43, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 30.1, - "mass": 0.72, - "unit": "kg" - } - ], - "components": [ - { - "id": 1111, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1112, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1113, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1114, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1115, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 111, - "url": "https://via.placeholder.com/400x300/06f8ff/ffffff?text=PureLife+Rice+Cooker+176O", - "description": "Rice Cooker product image" - } - ], - "created_at": "2025-03-21T09:16:11.453631Z", - "updated_at": "2025-05-24T09:16:11.453631Z" - }, - { - "id": 112, - "name": "ZenGear Rice Cooker 747A", - "description": "ZenGear Rice Cooker 747A is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.06, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1575, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 58.8, - "mass": 1.73, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 41.2, - "mass": 1.47, - "unit": "kg" - } - ], - "components": [ - { - "id": 1121, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1122, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1123, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1124, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1125, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 112, - "url": "https://via.placeholder.com/400x300/07f384/ffffff?text=ZenGear+Rice+Cooker+747A", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-07-16T09:16:11.453667Z", - "updated_at": "2025-06-09T09:16:11.453667Z" - }, - { - "id": 113, - "name": "AquaPro Toaster 535O", - "description": "AquaPro Toaster 535O is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.78, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1784, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 27.1, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 30.7, - "mass": 0.99, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 20.8, - "mass": 0.44, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 21.4, - "mass": 0.26, - "unit": "kg" - } - ], - "components": [ - { - "id": 1131, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1132, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1133, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 113, - "url": "https://via.placeholder.com/400x300/051dd1/ffffff?text=AquaPro+Toaster+535O", - "description": "Toaster product image" - } - ], - "created_at": "2024-07-28T09:16:11.453706Z", - "updated_at": "2025-06-04T09:16:11.453706Z" - }, - { - "id": 114, - "name": "NeoCook Iron 558J", - "description": "NeoCook Iron 558J is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.44, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1313, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 8.9, - "mass": 0.21, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 41.1, - "mass": 1.2, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 30.6, - "mass": 1.05, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 19.4, - "mass": 0.25, - "unit": "kg" - } - ], - "components": [ - { - "id": 1141, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 1142, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 114, - "url": "https://via.placeholder.com/400x300/0d56eb/ffffff?text=NeoCook+Iron+558J", - "description": "Iron product image" - } - ], - "created_at": "2025-03-19T09:16:11.453766Z", - "updated_at": "2025-05-06T09:16:11.453766Z" - }, - { - "id": 115, - "name": "EcoTech Iron 827F", - "description": "EcoTech Iron 827F is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.7, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1995, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 48.3, - "mass": 1.46, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 51.7, - "mass": 0.73, - "unit": "kg" - } - ], - "components": [ - { - "id": 1151, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 1152, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 115, - "url": "https://via.placeholder.com/400x300/0491fe/ffffff?text=EcoTech+Iron+827F", - "description": "Iron product image" - } - ], - "created_at": "2024-04-16T09:16:11.453823Z", - "updated_at": "2025-06-23T09:16:11.453823Z" - }, - { - "id": 116, - "name": "NeoCook Toaster 570V", - "description": "NeoCook Toaster 570V is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.04, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1252, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 19.9, - "mass": 0.64, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 30.7, - "mass": 0.46, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 21.6, - "mass": 0.49, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 27.8, - "mass": 0.97, - "unit": "kg" - } - ], - "components": [ - { - "id": 1161, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1162, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1163, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 116, - "url": "https://via.placeholder.com/400x300/0a3b61/ffffff?text=NeoCook+Toaster+570V", - "description": "Toaster product image" - } - ], - "created_at": "2024-04-24T09:16:11.453896Z", - "updated_at": "2025-04-30T09:16:11.453896Z" - }, - { - "id": 117, - "name": "EcoTech Fan 392G", - "description": "EcoTech Fan 392G is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.47, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1361, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 12.2, - "mass": 0.35, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 26.7, - "mass": 0.56, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 20.0, - "mass": 0.61, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 41.1, - "mass": 0.93, - "unit": "kg" - } - ], - "components": [ - { - "id": 1171, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 1172, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 1173, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 1174, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - }, - { - "id": 1175, - "name": "Fan Component 5", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 117, - "url": "https://via.placeholder.com/400x300/06785f/ffffff?text=EcoTech+Fan+392G", - "description": "Fan product image" - } - ], - "created_at": "2024-05-15T09:16:11.454124Z", - "updated_at": "2025-05-08T09:16:11.454124Z" - }, - { - "id": 118, - "name": "EcoTech Monitor 661X", - "description": "EcoTech Monitor 661X is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.04, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1126, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 47.7, - "mass": 0.54, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 52.3, - "mass": 0.95, - "unit": "kg" - } - ], - "components": [ - { - "id": 1181, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1182, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1183, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1184, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 118, - "url": "https://via.placeholder.com/400x300/03d2a7/ffffff?text=EcoTech+Monitor+661X", - "description": "Monitor product image" - } - ], - "created_at": "2024-10-31T09:16:11.454397Z", - "updated_at": "2025-05-15T09:16:11.454397Z" - }, - { - "id": 119, - "name": "CleanWave Fan 528A", - "description": "CleanWave Fan 528A is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.95, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1294, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 12.0, - "mass": 0.16, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 35.4, - "mass": 1.31, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 16.5, - "mass": 0.25, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 36.1, - "mass": 0.69, - "unit": "kg" - } - ], - "components": [ - { - "id": 1191, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 1192, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 1193, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 1194, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - }, - { - "id": 1195, - "name": "Fan Component 5", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 119, - "url": "https://via.placeholder.com/400x300/03e948/ffffff?text=CleanWave+Fan+528A", - "description": "Fan product image" - } - ], - "created_at": "2025-04-07T09:16:11.454813Z", - "updated_at": "2025-06-12T09:16:11.454813Z" - }, - { - "id": 120, - "name": "AquaPro Blender 433Z", - "description": "AquaPro Blender 433Z is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.26, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1899, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 31.4, - "mass": 0.84, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 31.4, - "mass": 0.85, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 31.9, - "mass": 0.6, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 5.3, - "mass": 0.15, - "unit": "kg" - } - ], - "components": [ - { - "id": 1201, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 1202, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 120, - "url": "https://via.placeholder.com/400x300/06af50/ffffff?text=AquaPro+Blender+433Z", - "description": "Blender product image" - } - ], - "created_at": "2024-10-14T09:16:11.454916Z", - "updated_at": "2025-07-21T09:16:11.454916Z" - }, - { - "id": 121, - "name": "CleanWave Kettle 614T", - "description": "CleanWave Kettle 614T is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.74, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1292, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 11.5, - "mass": 0.2, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 48.1, - "mass": 0.75, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 40.4, - "mass": 0.85, - "unit": "kg" - } - ], - "components": [ - { - "id": 1211, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1212, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1213, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1214, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1215, - "name": "Kettle Component 5", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 121, - "url": "https://via.placeholder.com/400x300/0afe96/ffffff?text=CleanWave+Kettle+614T", - "description": "Kettle product image" - } - ], - "created_at": "2025-01-09T09:16:11.454988Z", - "updated_at": "2025-07-21T09:16:11.454988Z" - }, - { - "id": 122, - "name": "NeoCook Coffee Maker 892J", - "description": "NeoCook Coffee Maker 892J is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.21, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1383, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 27.4, - "mass": 0.66, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 22.2, - "mass": 0.73, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 23.7, - "mass": 0.39, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 26.7, - "mass": 0.3, - "unit": "kg" - } - ], - "components": [ - { - "id": 1221, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1222, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 122, - "url": "https://via.placeholder.com/400x300/0bf37c/ffffff?text=NeoCook+Coffee+Maker+892J", - "description": "Coffee Maker product image" - } - ], - "created_at": "2025-03-25T09:16:11.455056Z", - "updated_at": "2025-07-12T09:16:11.455056Z" - }, - { - "id": 123, - "name": "CleanWave Fan 298P", - "description": "CleanWave Fan 298P is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.61, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1089, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 40.5, - "mass": 0.47, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 23.8, - "mass": 0.45, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 35.7, - "mass": 1.24, - "unit": "kg" - } - ], - "components": [ - { - "id": 1231, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 1232, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 1233, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 123, - "url": "https://via.placeholder.com/400x300/0979a8/ffffff?text=CleanWave+Fan+298P", - "description": "Fan product image" - } - ], - "created_at": "2025-02-02T09:16:11.455114Z", - "updated_at": "2025-06-17T09:16:11.455114Z" - }, - { - "id": 124, - "name": "NeoCook Kettle 965Z", - "description": "NeoCook Kettle 965Z is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.64, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1928, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 16.9, - "mass": 0.43, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 27.1, - "mass": 0.3, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 45.8, - "mass": 1.0, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 10.2, - "mass": 0.17, - "unit": "kg" - } - ], - "components": [ - { - "id": 1241, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1242, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1243, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1244, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1245, - "name": "Kettle Component 5", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 124, - "url": "https://via.placeholder.com/400x300/0e07cc/ffffff?text=NeoCook+Kettle+965Z", - "description": "Kettle product image" - } - ], - "created_at": "2024-06-02T09:16:11.455190Z", - "updated_at": "2025-05-19T09:16:11.455190Z" - }, - { - "id": 125, - "name": "PureLife Toaster 428F", - "description": "PureLife Toaster 428F is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.37, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1647, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 16.8, - "mass": 0.26, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 33.6, - "mass": 1.31, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 22.7, - "mass": 0.26, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 26.9, - "mass": 0.54, - "unit": "kg" - } - ], - "components": [ - { - "id": 1251, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1252, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1253, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1254, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1255, - "name": "Toaster Component 5", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 125, - "url": "https://via.placeholder.com/400x300/0c7508/ffffff?text=PureLife+Toaster+428F", - "description": "Toaster product image" - } - ], - "created_at": "2024-03-29T09:16:11.455388Z", - "updated_at": "2025-05-04T09:16:11.455388Z" - }, - { - "id": 126, - "name": "ZenGear Kettle 889Q", - "description": "ZenGear Kettle 889Q is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.99, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1558, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 13.5, - "mass": 0.49, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 66.3, - "mass": 1.82, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 20.2, - "mass": 0.77, - "unit": "kg" - } - ], - "components": [ - { - "id": 1261, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1262, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 126, - "url": "https://via.placeholder.com/400x300/034a52/ffffff?text=ZenGear+Kettle+889Q", - "description": "Kettle product image" - } - ], - "created_at": "2024-05-30T09:16:11.455471Z", - "updated_at": "2025-06-15T09:16:11.455471Z" - }, - { - "id": 127, - "name": "SmartHome Air Purifier 934O", - "description": "SmartHome Air Purifier 934O is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.06, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2050, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 20.7, - "mass": 0.31, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 25.9, - "mass": 0.35, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 22.8, - "mass": 0.75, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 30.6, - "mass": 0.87, - "unit": "kg" - } - ], - "components": [ - { - "id": 1271, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 1272, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 1273, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 1274, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 1275, - "name": "Air Purifier Component 5", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 127, - "url": "https://via.placeholder.com/400x300/0f29cd/ffffff?text=SmartHome+Air+Purifier+934O", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-10-19T09:16:11.455605Z", - "updated_at": "2025-05-04T09:16:11.455605Z" - }, - { - "id": 128, - "name": "EcoTech Rice Cooker 420T", - "description": "EcoTech Rice Cooker 420T is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.16, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1892, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 42.0, - "mass": 1.02, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 10.1, - "mass": 0.12, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 47.9, - "mass": 0.76, - "unit": "kg" - } - ], - "components": [ - { - "id": 1281, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1282, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 128, - "url": "https://via.placeholder.com/400x300/056bba/ffffff?text=EcoTech+Rice+Cooker+420T", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-08-09T09:16:11.455824Z", - "updated_at": "2025-07-17T09:16:11.455824Z" - }, - { - "id": 129, - "name": "AquaPro Rice Cooker 402Y", - "description": "AquaPro Rice Cooker 402Y is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.63, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 921, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 55.9, - "mass": 1.08, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 44.1, - "mass": 0.86, - "unit": "kg" - } - ], - "components": [ - { - "id": 1291, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1292, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1293, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 129, - "url": "https://via.placeholder.com/400x300/03a221/ffffff?text=AquaPro+Rice+Cooker+402Y", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-08-16T09:16:11.455917Z", - "updated_at": "2025-07-16T09:16:11.455917Z" - }, - { - "id": 130, - "name": "AquaPro Monitor 295C", - "description": "AquaPro Monitor 295C is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.96, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1157, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 36.9, - "mass": 1.11, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 24.3, - "mass": 0.83, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 38.8, - "mass": 0.43, - "unit": "kg" - } - ], - "components": [ - { - "id": 1301, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1302, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1303, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1304, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 130, - "url": "https://via.placeholder.com/400x300/07b9a6/ffffff?text=AquaPro+Monitor+295C", - "description": "Monitor product image" - } - ], - "created_at": "2024-10-22T09:16:11.456018Z", - "updated_at": "2025-05-17T09:16:11.456018Z" - }, - { - "id": 131, - "name": "NeoCook Coffee Maker 459Q", - "description": "NeoCook Coffee Maker 459Q is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.99, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1518, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 14.3, - "mass": 0.55, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 28.6, - "mass": 1.04, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 57.1, - "mass": 1.69, - "unit": "kg" - } - ], - "components": [ - { - "id": 1311, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1312, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 131, - "url": "https://via.placeholder.com/400x300/03bb89/ffffff?text=NeoCook+Coffee+Maker+459Q", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-11-28T09:16:11.456102Z", - "updated_at": "2025-05-09T09:16:11.456102Z" - }, - { - "id": 132, - "name": "NeoCook Monitor 111M", - "description": "NeoCook Monitor 111M is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.37, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2091, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 42.4, - "mass": 0.74, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 32.0, - "mass": 0.96, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 25.6, - "mass": 0.29, - "unit": "kg" - } - ], - "components": [ - { - "id": 1321, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1322, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1323, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1324, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1325, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 132, - "url": "https://via.placeholder.com/400x300/0d2198/ffffff?text=NeoCook+Monitor+111M", - "description": "Monitor product image" - } - ], - "created_at": "2024-04-26T09:16:11.456170Z", - "updated_at": "2025-07-22T09:16:11.456170Z" - }, - { - "id": 133, - "name": "AquaPro Iron 797U", - "description": "AquaPro Iron 797U is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.07, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1019, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 13.1, - "mass": 0.15, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 59.6, - "mass": 1.6, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 12.1, - "mass": 0.3, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 15.2, - "mass": 0.2, - "unit": "kg" - } - ], - "components": [ - { - "id": 1331, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 1332, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 1333, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 133, - "url": "https://via.placeholder.com/400x300/07d5df/ffffff?text=AquaPro+Iron+797U", - "description": "Iron product image" - } - ], - "created_at": "2024-08-01T09:16:11.456224Z", - "updated_at": "2025-04-29T09:16:11.456224Z" - }, - { - "id": 134, - "name": "ZenGear Monitor 834P", - "description": "ZenGear Monitor 834P is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.7, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 948, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 66.7, - "mass": 2.16, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 33.3, - "mass": 1.18, - "unit": "kg" - } - ], - "components": [ - { - "id": 1341, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1342, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1343, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1344, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 134, - "url": "https://via.placeholder.com/400x300/0703f2/ffffff?text=ZenGear+Monitor+834P", - "description": "Monitor product image" - } - ], - "created_at": "2024-12-19T09:16:11.456285Z", - "updated_at": "2025-04-25T09:16:11.456285Z" - }, - { - "id": 135, - "name": "PureLife Kettle 981Q", - "description": "PureLife Kettle 981Q is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.52, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1716, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 53.8, - "mass": 1.18, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 19.8, - "mass": 0.76, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 26.4, - "mass": 0.82, - "unit": "kg" - } - ], - "components": [ - { - "id": 1351, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1352, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1353, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1354, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 135, - "url": "https://via.placeholder.com/400x300/0e823d/ffffff?text=PureLife+Kettle+981Q", - "description": "Kettle product image" - } - ], - "created_at": "2025-03-08T09:16:11.456359Z", - "updated_at": "2025-07-10T09:16:11.456359Z" - }, - { - "id": 136, - "name": "SmartHome Humidifier 259V", - "description": "SmartHome Humidifier 259V is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.94, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2079, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 9.4, - "mass": 0.24, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 48.1, - "mass": 1.84, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 42.5, - "mass": 1.08, - "unit": "kg" - } - ], - "components": [ - { - "id": 1361, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1362, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1363, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1364, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1365, - "name": "Humidifier Component 5", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 136, - "url": "https://via.placeholder.com/400x300/0bf14d/ffffff?text=SmartHome+Humidifier+259V", - "description": "Humidifier product image" - } - ], - "created_at": "2025-02-24T09:16:11.456420Z", - "updated_at": "2025-07-31T09:16:11.456420Z" - }, - { - "id": 137, - "name": "ZenGear Coffee Maker 637O", - "description": "ZenGear Coffee Maker 637O is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.55, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1587, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 21.1, - "mass": 0.44, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 50.5, - "mass": 1.94, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 28.4, - "mass": 0.52, - "unit": "kg" - } - ], - "components": [ - { - "id": 1371, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1372, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1373, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1374, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1375, - "name": "Coffee Maker Component 5", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 137, - "url": "https://via.placeholder.com/400x300/09b645/ffffff?text=ZenGear+Coffee+Maker+637O", - "description": "Coffee Maker product image" - } - ], - "created_at": "2025-01-22T09:16:11.456499Z", - "updated_at": "2025-05-07T09:16:11.456499Z" - }, - { - "id": 138, - "name": "CleanWave Kettle 783H", - "description": "CleanWave Kettle 783H is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.98, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1534, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 37.3, - "mass": 1.38, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 30.3, - "mass": 0.57, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 32.4, - "mass": 0.84, - "unit": "kg" - } - ], - "components": [ - { - "id": 1381, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1382, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1383, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 138, - "url": "https://via.placeholder.com/400x300/09b511/ffffff?text=CleanWave+Kettle+783H", - "description": "Kettle product image" - } - ], - "created_at": "2024-11-08T09:16:11.456556Z", - "updated_at": "2025-07-19T09:16:11.456556Z" - }, - { - "id": 139, - "name": "ZenGear Rice Cooker 406O", - "description": "ZenGear Rice Cooker 406O is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.24, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1764, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 36.0, - "mass": 1.2, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 33.8, - "mass": 0.72, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 11.0, - "mass": 0.13, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 19.1, - "mass": 0.23, - "unit": "kg" - } - ], - "components": [ - { - "id": 1391, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1392, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1393, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1394, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1395, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 139, - "url": "https://via.placeholder.com/400x300/0c089d/ffffff?text=ZenGear+Rice+Cooker+406O", - "description": "Rice Cooker product image" - } - ], - "created_at": "2025-02-04T09:16:11.456803Z", - "updated_at": "2025-07-19T09:16:11.456803Z" - }, - { - "id": 140, - "name": "ChefMate Humidifier 939C", - "description": "ChefMate Humidifier 939C is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.5, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2065, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 31.7, - "mass": 1.12, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 68.3, - "mass": 2.11, - "unit": "kg" - } - ], - "components": [ - { - "id": 1401, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1402, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1403, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1404, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1405, - "name": "Humidifier Component 5", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 140, - "url": "https://via.placeholder.com/400x300/0cfc83/ffffff?text=ChefMate+Humidifier+939C", - "description": "Humidifier product image" - } - ], - "created_at": "2024-09-24T09:16:11.456881Z", - "updated_at": "2025-06-08T09:16:11.456881Z" - }, - { - "id": 141, - "name": "NeoCook Humidifier 447M", - "description": "NeoCook Humidifier 447M is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.72, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2132, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 67.1, - "mass": 2.21, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 32.9, - "mass": 0.95, - "unit": "kg" - } - ], - "components": [ - { - "id": 1411, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1412, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 141, - "url": "https://via.placeholder.com/400x300/0d3b4c/ffffff?text=NeoCook+Humidifier+447M", - "description": "Humidifier product image" - } - ], - "created_at": "2025-01-04T09:16:11.456942Z", - "updated_at": "2025-05-27T09:16:11.456942Z" - }, - { - "id": 142, - "name": "ZenGear Fan 908F", - "description": "ZenGear Fan 908F is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.29, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1815, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 27.9, - "mass": 0.8, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 72.1, - "mass": 2.31, - "unit": "kg" - } - ], - "components": [ - { - "id": 1421, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 1422, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 1423, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 1424, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 142, - "url": "https://via.placeholder.com/400x300/073224/ffffff?text=ZenGear+Fan+908F", - "description": "Fan product image" - } - ], - "created_at": "2025-03-18T09:16:11.456997Z", - "updated_at": "2025-05-11T09:16:11.456997Z" - }, - { - "id": 143, - "name": "PureLife Coffee Maker 690C", - "description": "PureLife Coffee Maker 690C is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.03, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 922, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 35.0, - "mass": 0.93, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 15.4, - "mass": 0.51, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 14.5, - "mass": 0.19, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 35.0, - "mass": 0.41, - "unit": "kg" - } - ], - "components": [ - { - "id": 1431, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1432, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1433, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 143, - "url": "https://via.placeholder.com/400x300/0807e3/ffffff?text=PureLife+Coffee+Maker+690C", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-11-08T09:16:11.457052Z", - "updated_at": "2025-04-26T09:16:11.457052Z" - }, - { - "id": 144, - "name": "EcoTech Blender 793S", - "description": "EcoTech Blender 793S is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.18, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2141, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 26.2, - "mass": 0.69, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 56.9, - "mass": 1.54, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 16.9, - "mass": 0.52, - "unit": "kg" - } - ], - "components": [ - { - "id": 1441, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 1442, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 1443, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 144, - "url": "https://via.placeholder.com/400x300/0d15de/ffffff?text=EcoTech+Blender+793S", - "description": "Blender product image" - } - ], - "created_at": "2024-08-01T09:16:11.457118Z", - "updated_at": "2025-06-08T09:16:11.457118Z" - }, - { - "id": 145, - "name": "CleanWave Kettle 715F", - "description": "CleanWave Kettle 715F is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.25, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1738, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 30.7, - "mass": 0.76, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 9.2, - "mass": 0.22, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 22.9, - "mass": 0.63, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 37.3, - "mass": 0.61, - "unit": "kg" - } - ], - "components": [ - { - "id": 1451, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1452, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1453, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1454, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1455, - "name": "Kettle Component 5", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 145, - "url": "https://via.placeholder.com/400x300/04e263/ffffff?text=CleanWave+Kettle+715F", - "description": "Kettle product image" - } - ], - "created_at": "2025-01-14T09:16:11.457163Z", - "updated_at": "2025-07-09T09:16:11.457163Z" - }, - { - "id": 146, - "name": "ChefMate Toaster 831R", - "description": "ChefMate Toaster 831R is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.88, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1013, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 27.3, - "mass": 0.4, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 15.3, - "mass": 0.37, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 30.7, - "mass": 0.76, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 26.7, - "mass": 0.7, - "unit": "kg" - } - ], - "components": [ - { - "id": 1461, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1462, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1463, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 146, - "url": "https://via.placeholder.com/400x300/0b1b13/ffffff?text=ChefMate+Toaster+831R", - "description": "Toaster product image" - } - ], - "created_at": "2024-12-09T09:16:11.457254Z", - "updated_at": "2025-07-17T09:16:11.457254Z" - }, - { - "id": 147, - "name": "EcoTech Coffee Maker 849K", - "description": "EcoTech Coffee Maker 849K is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.29, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1389, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 82.2, - "mass": 1.48, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 17.8, - "mass": 0.69, - "unit": "kg" - } - ], - "components": [ - { - "id": 1471, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1472, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 147, - "url": "https://via.placeholder.com/400x300/05f647/ffffff?text=EcoTech+Coffee+Maker+849K", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-12-01T09:16:11.457313Z", - "updated_at": "2025-05-03T09:16:11.457313Z" - }, - { - "id": 148, - "name": "SmartHome Fan 907H", - "description": "SmartHome Fan 907H is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.28, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1964, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 32.5, - "mass": 0.74, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 33.8, - "mass": 1.24, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 33.8, - "mass": 1.32, - "unit": "kg" - } - ], - "components": [ - { - "id": 1481, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 1482, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 1483, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 1484, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 148, - "url": "https://via.placeholder.com/400x300/0c3578/ffffff?text=SmartHome+Fan+907H", - "description": "Fan product image" - } - ], - "created_at": "2025-04-02T09:16:11.457377Z", - "updated_at": "2025-07-14T09:16:11.457377Z" - }, - { - "id": 149, - "name": "CleanWave Iron 405P", - "description": "CleanWave Iron 405P is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.5, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 811, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 29.7, - "mass": 0.5, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 70.3, - "mass": 2.04, - "unit": "kg" - } - ], - "components": [ - { - "id": 1491, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 1492, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 1493, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 149, - "url": "https://via.placeholder.com/400x300/060da7/ffffff?text=CleanWave+Iron+405P", - "description": "Iron product image" - } - ], - "created_at": "2025-04-13T09:16:11.457418Z", - "updated_at": "2025-06-25T09:16:11.457418Z" - }, - { - "id": 150, - "name": "PureLife Iron 151K", - "description": "PureLife Iron 151K is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.12, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1239, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 13.9, - "mass": 0.27, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 45.1, - "mass": 1.49, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 11.5, - "mass": 0.29, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 29.5, - "mass": 0.97, - "unit": "kg" - } - ], - "components": [ - { - "id": 1501, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 1502, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 1503, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 1504, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 150, - "url": "https://via.placeholder.com/400x300/054030/ffffff?text=PureLife+Iron+151K", - "description": "Iron product image" - } - ], - "created_at": "2025-03-09T09:16:11.457478Z", - "updated_at": "2025-05-10T09:16:11.457478Z" - }, - { - "id": 151, - "name": "ZenGear Fan 425K", - "description": "ZenGear Fan 425K is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.6, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1316, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 27.8, - "mass": 0.45, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 72.2, - "mass": 0.79, - "unit": "kg" - } - ], - "components": [ - { - "id": 1511, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 1512, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 1513, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 151, - "url": "https://via.placeholder.com/400x300/05c99a/ffffff?text=ZenGear+Fan+425K", - "description": "Fan product image" - } - ], - "created_at": "2024-05-30T09:16:11.457516Z", - "updated_at": "2025-06-02T09:16:11.457516Z" - }, - { - "id": 152, - "name": "EcoTech Fan 802P", - "description": "EcoTech Fan 802P is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.94, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1916, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 44.8, - "mass": 1.3, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 55.2, - "mass": 1.07, - "unit": "kg" - } - ], - "components": [ - { - "id": 1521, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 1522, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 1523, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 1524, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 152, - "url": "https://via.placeholder.com/400x300/039628/ffffff?text=EcoTech+Fan+802P", - "description": "Fan product image" - } - ], - "created_at": "2024-07-26T09:16:11.457559Z", - "updated_at": "2025-05-26T09:16:11.457559Z" - }, - { - "id": 153, - "name": "NeoCook Iron 387O", - "description": "NeoCook Iron 387O is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.91, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 900, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 21.5, - "mass": 0.38, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 56.9, - "mass": 1.05, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 21.5, - "mass": 0.68, - "unit": "kg" - } - ], - "components": [ - { - "id": 1531, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 1532, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 1533, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 153, - "url": "https://via.placeholder.com/400x300/03d8f1/ffffff?text=NeoCook+Iron+387O", - "description": "Iron product image" - } - ], - "created_at": "2025-03-23T09:16:11.457614Z", - "updated_at": "2025-07-17T09:16:11.457614Z" - }, - { - "id": 154, - "name": "ChefMate Rice Cooker 645N", - "description": "ChefMate Rice Cooker 645N is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.89, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 950, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 21.1, - "mass": 0.82, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 78.9, - "mass": 3.09, - "unit": "kg" - } - ], - "components": [ - { - "id": 1541, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1542, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1543, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1544, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1545, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 154, - "url": "https://via.placeholder.com/400x300/05f50e/ffffff?text=ChefMate+Rice+Cooker+645N", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-03-24T09:16:11.457658Z", - "updated_at": "2025-05-24T09:16:11.457658Z" - }, - { - "id": 155, - "name": "SmartHome Coffee Maker 347W", - "description": "SmartHome Coffee Maker 347W is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.12, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1116, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 44.3, - "mass": 1.0, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 55.7, - "mass": 0.85, - "unit": "kg" - } - ], - "components": [ - { - "id": 1551, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1552, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1553, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1554, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1555, - "name": "Coffee Maker Component 5", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 155, - "url": "https://via.placeholder.com/400x300/09b7f2/ffffff?text=SmartHome+Coffee+Maker+347W", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-06-06T09:16:11.457697Z", - "updated_at": "2025-05-31T09:16:11.457697Z" - }, - { - "id": 156, - "name": "SmartHome Humidifier 635C", - "description": "SmartHome Humidifier 635C is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.99, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1267, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 26.7, - "mass": 0.39, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 13.3, - "mass": 0.32, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 60.0, - "mass": 1.28, - "unit": "kg" - } - ], - "components": [ - { - "id": 1561, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1562, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1563, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1564, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1565, - "name": "Humidifier Component 5", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 156, - "url": "https://via.placeholder.com/400x300/08a081/ffffff?text=SmartHome+Humidifier+635C", - "description": "Humidifier product image" - } - ], - "created_at": "2025-02-02T09:16:11.457947Z", - "updated_at": "2025-06-06T09:16:11.457947Z" - }, - { - "id": 157, - "name": "PureLife Iron 639P", - "description": "PureLife Iron 639P is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.06, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1800, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 28.0, - "mass": 0.86, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 72.0, - "mass": 1.99, - "unit": "kg" - } - ], - "components": [ - { - "id": 1571, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 1572, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 1573, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 157, - "url": "https://via.placeholder.com/400x300/08ca64/ffffff?text=PureLife+Iron+639P", - "description": "Iron product image" - } - ], - "created_at": "2025-03-31T09:16:11.458028Z", - "updated_at": "2025-06-02T09:16:11.458028Z" - }, - { - "id": 158, - "name": "PureLife Coffee Maker 399D", - "description": "PureLife Coffee Maker 399D is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.85, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1604, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 31.2, - "mass": 1.14, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 44.1, - "mass": 1.14, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 24.7, - "mass": 0.38, - "unit": "kg" - } - ], - "components": [ - { - "id": 1581, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1582, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 158, - "url": "https://via.placeholder.com/400x300/0abfac/ffffff?text=PureLife+Coffee+Maker+399D", - "description": "Coffee Maker product image" - } - ], - "created_at": "2025-01-28T09:16:11.458092Z", - "updated_at": "2025-04-30T09:16:11.458092Z" - }, - { - "id": 159, - "name": "ZenGear Iron 956L", - "description": "ZenGear Iron 956L is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.97, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1118, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 30.1, - "mass": 0.43, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 32.2, - "mass": 0.79, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 17.5, - "mass": 0.38, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 20.2, - "mass": 0.58, - "unit": "kg" - } - ], - "components": [ - { - "id": 1591, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 1592, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 1593, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 159, - "url": "https://via.placeholder.com/400x300/097bdb/ffffff?text=ZenGear+Iron+956L", - "description": "Iron product image" - } - ], - "created_at": "2024-12-05T09:16:11.458161Z", - "updated_at": "2025-07-25T09:16:11.458161Z" - }, - { - "id": 160, - "name": "PureLife Air Purifier 644M", - "description": "PureLife Air Purifier 644M is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.08, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1495, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 39.7, - "mass": 0.93, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 11.1, - "mass": 0.38, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 20.6, - "mass": 0.6, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 28.6, - "mass": 1.0, - "unit": "kg" - } - ], - "components": [ - { - "id": 1601, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 1602, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 1603, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 1604, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 160, - "url": "https://via.placeholder.com/400x300/073e17/ffffff?text=PureLife+Air+Purifier+644M", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-08-24T09:16:11.458215Z", - "updated_at": "2025-06-09T09:16:11.458215Z" - }, - { - "id": 161, - "name": "ChefMate Coffee Maker 778K", - "description": "ChefMate Coffee Maker 778K is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.46, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1729, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 13.2, - "mass": 0.14, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 9.6, - "mass": 0.38, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 38.2, - "mass": 1.48, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 39.0, - "mass": 1.35, - "unit": "kg" - } - ], - "components": [ - { - "id": 1611, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1612, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1613, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1614, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1615, - "name": "Coffee Maker Component 5", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 161, - "url": "https://via.placeholder.com/400x300/01bace/ffffff?text=ChefMate+Coffee+Maker+778K", - "description": "Coffee Maker product image" - } - ], - "created_at": "2025-04-18T09:16:11.458338Z", - "updated_at": "2025-05-01T09:16:11.458338Z" - }, - { - "id": 162, - "name": "ChefMate Fan 148C", - "description": "ChefMate Fan 148C is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.36, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1754, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 37.1, - "mass": 1.42, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 40.9, - "mass": 1.48, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 22.0, - "mass": 0.22, - "unit": "kg" - } - ], - "components": [ - { - "id": 1621, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 1622, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 1623, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 1624, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 162, - "url": "https://via.placeholder.com/400x300/0d0929/ffffff?text=ChefMate+Fan+148C", - "description": "Fan product image" - } - ], - "created_at": "2024-05-26T09:16:11.458381Z", - "updated_at": "2025-05-22T09:16:11.458381Z" - }, - { - "id": 163, - "name": "SmartHome Fan 954Q", - "description": "SmartHome Fan 954Q is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.35, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1141, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 78.5, - "mass": 1.88, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 21.5, - "mass": 0.5, - "unit": "kg" - } - ], - "components": [ - { - "id": 1631, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 1632, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 1633, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 163, - "url": "https://via.placeholder.com/400x300/072ef0/ffffff?text=SmartHome+Fan+954Q", - "description": "Fan product image" - } - ], - "created_at": "2024-08-17T09:16:11.458431Z", - "updated_at": "2025-06-02T09:16:11.458431Z" - }, - { - "id": 164, - "name": "PureLife Blender 806F", - "description": "PureLife Blender 806F is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.15, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 993, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 40.8, - "mass": 0.46, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 45.6, - "mass": 1.31, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 13.6, - "mass": 0.52, - "unit": "kg" - } - ], - "components": [ - { - "id": 1641, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 1642, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 1643, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 1644, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 1645, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 164, - "url": "https://via.placeholder.com/400x300/03a7bb/ffffff?text=PureLife+Blender+806F", - "description": "Blender product image" - } - ], - "created_at": "2025-01-08T09:16:11.458628Z", - "updated_at": "2025-07-14T09:16:11.458628Z" - }, - { - "id": 165, - "name": "CleanWave Blender 124H", - "description": "CleanWave Blender 124H is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.07, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2192, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 39.6, - "mass": 0.67, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 60.4, - "mass": 2.01, - "unit": "kg" - } - ], - "components": [ - { - "id": 1651, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 1652, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 1653, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 1654, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 1655, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 165, - "url": "https://via.placeholder.com/400x300/019b00/ffffff?text=CleanWave+Blender+124H", - "description": "Blender product image" - } - ], - "created_at": "2024-12-12T09:16:11.458679Z", - "updated_at": "2025-07-03T09:16:11.458679Z" - }, - { - "id": 166, - "name": "ChefMate Monitor 543X", - "description": "ChefMate Monitor 543X is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.8, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1043, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 27.9, - "mass": 0.58, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 17.3, - "mass": 0.37, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 14.4, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 40.4, - "mass": 1.46, - "unit": "kg" - } - ], - "components": [ - { - "id": 1661, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1662, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 166, - "url": "https://via.placeholder.com/400x300/0d7224/ffffff?text=ChefMate+Monitor+543X", - "description": "Monitor product image" - } - ], - "created_at": "2025-01-27T09:16:11.458720Z", - "updated_at": "2025-06-05T09:16:11.458720Z" - }, - { - "id": 167, - "name": "ZenGear Iron 514I", - "description": "ZenGear Iron 514I is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.01, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1492, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 56.5, - "mass": 1.8, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 43.5, - "mass": 1.63, - "unit": "kg" - } - ], - "components": [ - { - "id": 1671, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 1672, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 167, - "url": "https://via.placeholder.com/400x300/0b2876/ffffff?text=ZenGear+Iron+514I", - "description": "Iron product image" - } - ], - "created_at": "2025-02-16T09:16:11.458757Z", - "updated_at": "2025-06-21T09:16:11.458757Z" - }, - { - "id": 168, - "name": "AquaPro Blender 568J", - "description": "AquaPro Blender 568J is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.07, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 932, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 10.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 7.0, - "mass": 0.11, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 42.3, - "mass": 0.65, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 40.8, - "mass": 1.61, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 9.9, - "mass": 0.16, - "unit": "kg" - } - ], - "components": [ - { - "id": 1681, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 1682, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 1683, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 168, - "url": "https://via.placeholder.com/400x300/0a0983/ffffff?text=AquaPro+Blender+568J", - "description": "Blender product image" - } - ], - "created_at": "2024-04-29T09:16:11.458791Z", - "updated_at": "2025-07-02T09:16:11.458791Z" - }, - { - "id": 169, - "name": "SmartHome Coffee Maker 447B", - "description": "SmartHome Coffee Maker 447B is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.03, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1942, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 38.8, - "mass": 0.77, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 9.5, - "mass": 0.13, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 25.9, - "mass": 0.77, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 25.9, - "mass": 0.85, - "unit": "kg" - } - ], - "components": [ - { - "id": 1691, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1692, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 169, - "url": "https://via.placeholder.com/400x300/05bf0b/ffffff?text=SmartHome+Coffee+Maker+447B", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-03-19T09:16:11.458829Z", - "updated_at": "2025-07-14T09:16:11.458829Z" - }, - { - "id": 170, - "name": "ZenGear Humidifier 398I", - "description": "ZenGear Humidifier 398I is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.81, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1109, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 27.3, - "mass": 1.0, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 30.3, - "mass": 0.84, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 42.4, - "mass": 1.65, - "unit": "kg" - } - ], - "components": [ - { - "id": 1701, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1702, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1703, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1704, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 170, - "url": "https://via.placeholder.com/400x300/061b21/ffffff?text=ZenGear+Humidifier+398I", - "description": "Humidifier product image" - } - ], - "created_at": "2025-04-18T09:16:11.458866Z", - "updated_at": "2025-06-16T09:16:11.458866Z" - }, - { - "id": 171, - "name": "NeoCook Toaster 221Z", - "description": "NeoCook Toaster 221Z is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.89, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1265, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 46.4, - "mass": 0.8, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 53.6, - "mass": 0.54, - "unit": "kg" - } - ], - "components": [ - { - "id": 1711, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1712, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1713, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1714, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - }, - { - "id": 1715, - "name": "Toaster Component 5", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 171, - "url": "https://via.placeholder.com/400x300/08cc4b/ffffff?text=NeoCook+Toaster+221Z", - "description": "Toaster product image" - } - ], - "created_at": "2024-11-01T09:16:11.458898Z", - "updated_at": "2025-07-30T09:16:11.458898Z" - }, - { - "id": 172, - "name": "SmartHome Coffee Maker 126B", - "description": "SmartHome Coffee Maker 126B is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.85, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1736, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 29.5, - "mass": 1.11, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 29.5, - "mass": 0.73, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 40.9, - "mass": 0.61, - "unit": "kg" - } - ], - "components": [ - { - "id": 1721, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1722, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1723, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 172, - "url": "https://via.placeholder.com/400x300/074699/ffffff?text=SmartHome+Coffee+Maker+126B", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-12-15T09:16:11.458933Z", - "updated_at": "2025-05-08T09:16:11.458933Z" - }, - { - "id": 173, - "name": "ZenGear Fan 130W", - "description": "ZenGear Fan 130W is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.15, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1338, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 22.6, - "mass": 0.8, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 28.0, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 49.5, - "mass": 1.18, - "unit": "kg" - } - ], - "components": [ - { - "id": 1731, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 1732, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 173, - "url": "https://via.placeholder.com/400x300/033a12/ffffff?text=ZenGear+Fan+130W", - "description": "Fan product image" - } - ], - "created_at": "2024-12-22T09:16:11.458986Z", - "updated_at": "2025-07-22T09:16:11.458986Z" - }, - { - "id": 174, - "name": "CleanWave Iron 282V", - "description": "CleanWave Iron 282V is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.31, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 871, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 49.0, - "mass": 1.28, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 51.0, - "mass": 1.11, - "unit": "kg" - } - ], - "components": [ - { - "id": 1741, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 1742, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 174, - "url": "https://via.placeholder.com/400x300/0659ce/ffffff?text=CleanWave+Iron+282V", - "description": "Iron product image" - } - ], - "created_at": "2024-04-23T09:16:11.459039Z", - "updated_at": "2025-06-21T09:16:11.459039Z" - }, - { - "id": 175, - "name": "ZenGear Kettle 134D", - "description": "ZenGear Kettle 134D is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.38, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1026, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 22.6, - "mass": 0.4, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 39.1, - "mass": 0.98, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 38.3, - "mass": 1.42, - "unit": "kg" - } - ], - "components": [ - { - "id": 1751, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1752, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 175, - "url": "https://via.placeholder.com/400x300/0e1694/ffffff?text=ZenGear+Kettle+134D", - "description": "Kettle product image" - } - ], - "created_at": "2024-06-16T09:16:11.459086Z", - "updated_at": "2025-06-28T09:16:11.459086Z" - }, - { - "id": 176, - "name": "ChefMate Monitor 551S", - "description": "ChefMate Monitor 551S is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.33, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1685, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 49.0, - "mass": 0.53, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 51.0, - "mass": 1.22, - "unit": "kg" - } - ], - "components": [ - { - "id": 1761, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1762, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1763, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1764, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1765, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 176, - "url": "https://via.placeholder.com/400x300/0e72cd/ffffff?text=ChefMate+Monitor+551S", - "description": "Monitor product image" - } - ], - "created_at": "2024-09-24T09:16:11.459141Z", - "updated_at": "2025-07-10T09:16:11.459141Z" - }, - { - "id": 177, - "name": "EcoTech Fan 100N", - "description": "EcoTech Fan 100N is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.36, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2085, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 37.8, - "mass": 1.49, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 18.9, - "mass": 0.21, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 43.2, - "mass": 0.58, - "unit": "kg" - } - ], - "components": [ - { - "id": 1771, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 1772, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 1773, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 1774, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 177, - "url": "https://via.placeholder.com/400x300/05ada2/ffffff?text=EcoTech+Fan+100N", - "description": "Fan product image" - } - ], - "created_at": "2024-10-23T09:16:11.459190Z", - "updated_at": "2025-05-09T09:16:11.459190Z" - }, - { - "id": 178, - "name": "ChefMate Humidifier 376F", - "description": "ChefMate Humidifier 376F is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.03, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1596, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 41.4, - "mass": 0.54, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 58.6, - "mass": 2.32, - "unit": "kg" - } - ], - "components": [ - { - "id": 1781, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1782, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1783, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 178, - "url": "https://via.placeholder.com/400x300/0c6482/ffffff?text=ChefMate+Humidifier+376F", - "description": "Humidifier product image" - } - ], - "created_at": "2025-04-04T09:16:11.459226Z", - "updated_at": "2025-07-25T09:16:11.459226Z" - }, - { - "id": 179, - "name": "ChefMate Monitor 876P", - "description": "ChefMate Monitor 876P is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.56, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1998, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 46.7, - "mass": 1.4, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 53.3, - "mass": 1.8, - "unit": "kg" - } - ], - "components": [ - { - "id": 1791, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1792, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1793, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1794, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 179, - "url": "https://via.placeholder.com/400x300/069c1b/ffffff?text=ChefMate+Monitor+876P", - "description": "Monitor product image" - } - ], - "created_at": "2025-03-07T09:16:11.459264Z", - "updated_at": "2025-06-02T09:16:11.459264Z" - }, - { - "id": 180, - "name": "PureLife Blender 523P", - "description": "PureLife Blender 523P is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.07, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1200, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 65.1, - "mass": 2.49, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 34.9, - "mass": 1.13, - "unit": "kg" - } - ], - "components": [ - { - "id": 1801, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 1802, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 1803, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 180, - "url": "https://via.placeholder.com/400x300/07724e/ffffff?text=PureLife+Blender+523P", - "description": "Blender product image" - } - ], - "created_at": "2025-01-11T09:16:11.459308Z", - "updated_at": "2025-07-21T09:16:11.459308Z" - }, - { - "id": 181, - "name": "PureLife Rice Cooker 749I", - "description": "PureLife Rice Cooker 749I is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.58, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 894, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 16.4, - "mass": 0.37, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 22.4, - "mass": 0.3, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 32.2, - "mass": 0.65, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 28.9, - "mass": 1.12, - "unit": "kg" - } - ], - "components": [ - { - "id": 1811, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1812, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1813, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1814, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 181, - "url": "https://via.placeholder.com/400x300/0bc755/ffffff?text=PureLife+Rice+Cooker+749I", - "description": "Rice Cooker product image" - } - ], - "created_at": "2025-01-29T09:16:11.459366Z", - "updated_at": "2025-04-25T09:16:11.459366Z" - }, - { - "id": 182, - "name": "NeoCook Blender 829D", - "description": "NeoCook Blender 829D is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.11, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2108, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 43.9, - "mass": 0.92, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 29.8, - "mass": 0.87, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 26.3, - "mass": 0.85, - "unit": "kg" - } - ], - "components": [ - { - "id": 1821, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 1822, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 182, - "url": "https://via.placeholder.com/400x300/07ebac/ffffff?text=NeoCook+Blender+829D", - "description": "Blender product image" - } - ], - "created_at": "2024-10-14T09:16:11.459428Z", - "updated_at": "2025-06-11T09:16:11.459428Z" - }, - { - "id": 183, - "name": "SmartHome Monitor 878G", - "description": "SmartHome Monitor 878G is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.42, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1969, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 31.1, - "mass": 0.89, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 10.9, - "mass": 0.36, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 20.2, - "mass": 0.65, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 37.8, - "mass": 0.45, - "unit": "kg" - } - ], - "components": [ - { - "id": 1831, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1832, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1833, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1834, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 183, - "url": "https://via.placeholder.com/400x300/0eeb79/ffffff?text=SmartHome+Monitor+878G", - "description": "Monitor product image" - } - ], - "created_at": "2024-08-27T09:16:11.459490Z", - "updated_at": "2025-04-28T09:16:11.459490Z" - }, - { - "id": 184, - "name": "NeoCook Coffee Maker 492J", - "description": "NeoCook Coffee Maker 492J is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.15, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1698, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 32.9, - "mass": 1.08, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 31.1, - "mass": 1.21, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 36.0, - "mass": 0.56, - "unit": "kg" - } - ], - "components": [ - { - "id": 1841, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1842, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1843, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1844, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 1845, - "name": "Coffee Maker Component 5", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 184, - "url": "https://via.placeholder.com/400x300/0cea6a/ffffff?text=NeoCook+Coffee+Maker+492J", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-11-09T09:16:11.459534Z", - "updated_at": "2025-05-18T09:16:11.459534Z" - }, - { - "id": 185, - "name": "NeoCook Blender 191L", - "description": "NeoCook Blender 191L is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.07, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1870, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 33.5, - "mass": 0.52, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 7.2, - "mass": 0.27, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 33.5, - "mass": 1.3, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 25.7, - "mass": 0.99, - "unit": "kg" - } - ], - "components": [ - { - "id": 1851, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 1852, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 1853, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 1854, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 1855, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 185, - "url": "https://via.placeholder.com/400x300/0bfcef/ffffff?text=NeoCook+Blender+191L", - "description": "Blender product image" - } - ], - "created_at": "2025-03-10T09:16:11.459585Z", - "updated_at": "2025-07-23T09:16:11.459585Z" - }, - { - "id": 186, - "name": "NeoCook Iron 604K", - "description": "NeoCook Iron 604K is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.01, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1356, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 25.5, - "mass": 0.99, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 42.9, - "mass": 0.73, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 31.6, - "mass": 0.41, - "unit": "kg" - } - ], - "components": [ - { - "id": 1861, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 1862, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 186, - "url": "https://via.placeholder.com/400x300/0a08f5/ffffff?text=NeoCook+Iron+604K", - "description": "Iron product image" - } - ], - "created_at": "2024-12-10T09:16:11.459621Z", - "updated_at": "2025-05-07T09:16:11.459621Z" - }, - { - "id": 187, - "name": "ChefMate Iron 615G", - "description": "ChefMate Iron 615G is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.51, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1227, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 10.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 9.4, - "mass": 0.36, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 30.4, - "mass": 0.37, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 26.3, - "mass": 0.32, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 33.9, - "mass": 1.02, - "unit": "kg" - } - ], - "components": [ - { - "id": 1871, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 1872, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 187, - "url": "https://via.placeholder.com/400x300/048ca6/ffffff?text=ChefMate+Iron+615G", - "description": "Iron product image" - } - ], - "created_at": "2024-03-26T09:16:11.459662Z", - "updated_at": "2025-06-06T09:16:11.459662Z" - }, - { - "id": 188, - "name": "NeoCook Rice Cooker 721C", - "description": "NeoCook Rice Cooker 721C is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.62, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1689, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 43.0, - "mass": 0.98, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 40.2, - "mass": 1.34, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 16.8, - "mass": 0.37, - "unit": "kg" - } - ], - "components": [ - { - "id": 1881, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1882, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1883, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1884, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 1885, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 188, - "url": "https://via.placeholder.com/400x300/03116a/ffffff?text=NeoCook+Rice+Cooker+721C", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-04-17T09:16:11.459724Z", - "updated_at": "2025-05-24T09:16:11.459724Z" - }, - { - "id": 189, - "name": "ChefMate Monitor 877K", - "description": "ChefMate Monitor 877K is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.61, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1175, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 32.8, - "mass": 0.89, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 12.0, - "mass": 0.31, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 30.4, - "mass": 1.16, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 24.8, - "mass": 0.58, - "unit": "kg" - } - ], - "components": [ - { - "id": 1891, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1892, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 189, - "url": "https://via.placeholder.com/400x300/05a4a4/ffffff?text=ChefMate+Monitor+877K", - "description": "Monitor product image" - } - ], - "created_at": "2024-06-19T09:16:11.459760Z", - "updated_at": "2025-05-24T09:16:11.459760Z" - }, - { - "id": 190, - "name": "AquaPro Humidifier 762F", - "description": "AquaPro Humidifier 762F is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.49, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1156, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 40.0, - "mass": 0.55, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 41.2, - "mass": 0.72, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 18.8, - "mass": 0.32, - "unit": "kg" - } - ], - "components": [ - { - "id": 1901, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1902, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1903, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1904, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 190, - "url": "https://via.placeholder.com/400x300/03f749/ffffff?text=AquaPro+Humidifier+762F", - "description": "Humidifier product image" - } - ], - "created_at": "2024-09-01T09:16:11.459793Z", - "updated_at": "2025-07-20T09:16:11.459793Z" - }, - { - "id": 191, - "name": "NeoCook Humidifier 113P", - "description": "NeoCook Humidifier 113P is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.25, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2075, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 55.0, - "mass": 1.87, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 45.0, - "mass": 1.38, - "unit": "kg" - } - ], - "components": [ - { - "id": 1911, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1912, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 191, - "url": "https://via.placeholder.com/400x300/0af212/ffffff?text=NeoCook+Humidifier+113P", - "description": "Humidifier product image" - } - ], - "created_at": "2024-06-25T09:16:11.459844Z", - "updated_at": "2025-05-10T09:16:11.459844Z" - }, - { - "id": 192, - "name": "PureLife Fan 544N", - "description": "PureLife Fan 544N is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.33, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1244, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 24.2, - "mass": 0.35, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 42.4, - "mass": 1.67, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 33.3, - "mass": 0.94, - "unit": "kg" - } - ], - "components": [ - { - "id": 1921, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 1922, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 192, - "url": "https://via.placeholder.com/400x300/0a688d/ffffff?text=PureLife+Fan+544N", - "description": "Fan product image" - } - ], - "created_at": "2024-06-04T09:16:11.459889Z", - "updated_at": "2025-06-21T09:16:11.459889Z" - }, - { - "id": 193, - "name": "CleanWave Monitor 173R", - "description": "CleanWave Monitor 173R is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.85, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 920, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 42.0, - "mass": 1.53, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 29.4, - "mass": 0.39, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 28.7, - "mass": 0.98, - "unit": "kg" - } - ], - "components": [ - { - "id": 1931, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1932, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1933, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 193, - "url": "https://via.placeholder.com/400x300/01c58a/ffffff?text=CleanWave+Monitor+173R", - "description": "Monitor product image" - } - ], - "created_at": "2025-02-06T09:16:11.459924Z", - "updated_at": "2025-07-06T09:16:11.459924Z" - }, - { - "id": 194, - "name": "ChefMate Monitor 848C", - "description": "ChefMate Monitor 848C is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.55, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1298, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 60.4, - "mass": 2.13, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 39.6, - "mass": 0.75, - "unit": "kg" - } - ], - "components": [ - { - "id": 1941, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1942, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1943, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 194, - "url": "https://via.placeholder.com/400x300/0d129a/ffffff?text=ChefMate+Monitor+848C", - "description": "Monitor product image" - } - ], - "created_at": "2024-08-11T09:16:11.459950Z", - "updated_at": "2025-07-26T09:16:11.459950Z" - }, - { - "id": 195, - "name": "NeoCook Monitor 299Z", - "description": "NeoCook Monitor 299Z is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.35, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1163, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 45.0, - "mass": 1.69, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 55.0, - "mass": 2.01, - "unit": "kg" - } - ], - "components": [ - { - "id": 1951, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 1952, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 195, - "url": "https://via.placeholder.com/400x300/0475c3/ffffff?text=NeoCook+Monitor+299Z", - "description": "Monitor product image" - } - ], - "created_at": "2024-05-09T09:16:11.459985Z", - "updated_at": "2025-05-02T09:16:11.459985Z" - }, - { - "id": 196, - "name": "ZenGear Kettle 605E", - "description": "ZenGear Kettle 605E is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.13, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 882, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 23.4, - "mass": 0.5, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 22.8, - "mass": 0.91, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 26.1, - "mass": 0.68, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 27.7, - "mass": 0.46, - "unit": "kg" - } - ], - "components": [ - { - "id": 1961, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1962, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1963, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1964, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - }, - { - "id": 1965, - "name": "Kettle Component 5", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 196, - "url": "https://via.placeholder.com/400x300/051679/ffffff?text=ZenGear+Kettle+605E", - "description": "Kettle product image" - } - ], - "created_at": "2024-10-22T09:16:11.460074Z", - "updated_at": "2025-07-08T09:16:11.460074Z" - }, - { - "id": 197, - "name": "ChefMate Fan 829A", - "description": "ChefMate Fan 829A is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.24, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1651, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 9.3, - "mass": 0.32, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 10.9, - "mass": 0.15, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 42.6, - "mass": 1.62, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 37.2, - "mass": 1.48, - "unit": "kg" - } - ], - "components": [ - { - "id": 1971, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 1972, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 1973, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 197, - "url": "https://via.placeholder.com/400x300/038a09/ffffff?text=ChefMate+Fan+829A", - "description": "Fan product image" - } - ], - "created_at": "2024-07-18T09:16:11.460140Z", - "updated_at": "2025-04-30T09:16:11.460140Z" - }, - { - "id": 198, - "name": "CleanWave Blender 175J", - "description": "CleanWave Blender 175J is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.03, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 829, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 15.2, - "mass": 0.16, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 42.8, - "mass": 1.11, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 23.9, - "mass": 0.46, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 18.1, - "mass": 0.69, - "unit": "kg" - } - ], - "components": [ - { - "id": 1981, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 1982, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 198, - "url": "https://via.placeholder.com/400x300/028e3f/ffffff?text=CleanWave+Blender+175J", - "description": "Blender product image" - } - ], - "created_at": "2025-03-31T09:16:11.460191Z", - "updated_at": "2025-07-07T09:16:11.460191Z" - }, - { - "id": 199, - "name": "ChefMate Humidifier 238X", - "description": "ChefMate Humidifier 238X is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.96, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1673, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 64.9, - "mass": 1.12, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 35.1, - "mass": 0.87, - "unit": "kg" - } - ], - "components": [ - { - "id": 1991, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1992, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 1993, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 199, - "url": "https://via.placeholder.com/400x300/05d368/ffffff?text=ChefMate+Humidifier+238X", - "description": "Humidifier product image" - } - ], - "created_at": "2025-04-07T09:16:11.460242Z", - "updated_at": "2025-06-09T09:16:11.460242Z" - }, - { - "id": 200, - "name": "AquaPro Fan 496Z", - "description": "AquaPro Fan 496Z is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.11, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2153, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 30.5, - "mass": 0.65, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 25.7, - "mass": 0.68, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 11.4, - "mass": 0.43, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 32.4, - "mass": 0.67, - "unit": "kg" - } - ], - "components": [ - { - "id": 2001, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 2002, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 200, - "url": "https://via.placeholder.com/400x300/034f85/ffffff?text=AquaPro+Fan+496Z", - "description": "Fan product image" - } - ], - "created_at": "2025-03-22T09:16:11.460292Z", - "updated_at": "2025-06-20T09:16:11.460292Z" - }, - { - "id": 201, - "name": "NeoCook Blender 309E", - "description": "NeoCook Blender 309E is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.15, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2087, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 38.2, - "mass": 0.96, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 16.8, - "mass": 0.39, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 45.0, - "mass": 1.6, - "unit": "kg" - } - ], - "components": [ - { - "id": 2011, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 2012, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 201, - "url": "https://via.placeholder.com/400x300/058ce4/ffffff?text=NeoCook+Blender+309E", - "description": "Blender product image" - } - ], - "created_at": "2024-09-09T09:16:11.460352Z", - "updated_at": "2025-06-24T09:16:11.460352Z" - }, - { - "id": 202, - "name": "NeoCook Kettle 631F", - "description": "NeoCook Kettle 631F is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.45, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 957, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 37.0, - "mass": 0.39, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 63.0, - "mass": 1.16, - "unit": "kg" - } - ], - "components": [ - { - "id": 2021, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2022, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2023, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2024, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2025, - "name": "Kettle Component 5", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 202, - "url": "https://via.placeholder.com/400x300/04c645/ffffff?text=NeoCook+Kettle+631F", - "description": "Kettle product image" - } - ], - "created_at": "2024-11-19T09:16:11.460397Z", - "updated_at": "2025-07-27T09:16:11.460397Z" - }, - { - "id": 203, - "name": "SmartHome Kettle 398Z", - "description": "SmartHome Kettle 398Z is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.36, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1592, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 21.6, - "mass": 0.61, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 20.1, - "mass": 0.59, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 25.4, - "mass": 0.28, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 32.8, - "mass": 0.76, - "unit": "kg" - } - ], - "components": [ - { - "id": 2031, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2032, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2033, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2034, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2035, - "name": "Kettle Component 5", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 203, - "url": "https://via.placeholder.com/400x300/04379f/ffffff?text=SmartHome+Kettle+398Z", - "description": "Kettle product image" - } - ], - "created_at": "2024-07-22T09:16:11.460453Z", - "updated_at": "2025-06-15T09:16:11.460453Z" - }, - { - "id": 204, - "name": "ChefMate Air Purifier 481K", - "description": "ChefMate Air Purifier 481K is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.4, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1486, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 28.3, - "mass": 0.46, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 40.0, - "mass": 1.25, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 12.4, - "mass": 0.14, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 19.3, - "mass": 0.75, - "unit": "kg" - } - ], - "components": [ - { - "id": 2041, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2042, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2043, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2044, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2045, - "name": "Air Purifier Component 5", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 204, - "url": "https://via.placeholder.com/400x300/0707d1/ffffff?text=ChefMate+Air+Purifier+481K", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-04-01T09:16:11.460520Z", - "updated_at": "2025-07-05T09:16:11.460520Z" - }, - { - "id": 205, - "name": "EcoTech Blender 294G", - "description": "EcoTech Blender 294G is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.52, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1636, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 33.6, - "mass": 0.74, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 30.0, - "mass": 0.37, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 36.4, - "mass": 0.88, - "unit": "kg" - } - ], - "components": [ - { - "id": 2051, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 2052, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 2053, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 2054, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 2055, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 205, - "url": "https://via.placeholder.com/400x300/093ae7/ffffff?text=EcoTech+Blender+294G", - "description": "Blender product image" - } - ], - "created_at": "2024-07-29T09:16:11.460570Z", - "updated_at": "2025-05-05T09:16:11.460570Z" - }, - { - "id": 206, - "name": "PureLife Kettle 641E", - "description": "PureLife Kettle 641E is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.21, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1325, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 71.2, - "mass": 1.36, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 28.8, - "mass": 0.72, - "unit": "kg" - } - ], - "components": [ - { - "id": 2061, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2062, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 206, - "url": "https://via.placeholder.com/400x300/071524/ffffff?text=PureLife+Kettle+641E", - "description": "Kettle product image" - } - ], - "created_at": "2024-09-16T09:16:11.460611Z", - "updated_at": "2025-05-13T09:16:11.460611Z" - }, - { - "id": 207, - "name": "ChefMate Fan 302I", - "description": "ChefMate Fan 302I is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.07, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1465, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 40.0, - "mass": 0.47, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 60.0, - "mass": 1.11, - "unit": "kg" - } - ], - "components": [ - { - "id": 2071, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 2072, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 2073, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 2074, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - }, - { - "id": 2075, - "name": "Fan Component 5", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 207, - "url": "https://via.placeholder.com/400x300/0c3abe/ffffff?text=ChefMate+Fan+302I", - "description": "Fan product image" - } - ], - "created_at": "2024-11-19T09:16:11.460661Z", - "updated_at": "2025-05-09T09:16:11.460661Z" - }, - { - "id": 208, - "name": "AquaPro Monitor 969U", - "description": "AquaPro Monitor 969U is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.81, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1779, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 56.1, - "mass": 0.68, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 43.9, - "mass": 1.01, - "unit": "kg" - } - ], - "components": [ - { - "id": 2081, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2082, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2083, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2084, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 208, - "url": "https://via.placeholder.com/400x300/0481cb/ffffff?text=AquaPro+Monitor+969U", - "description": "Monitor product image" - } - ], - "created_at": "2024-11-09T09:16:11.460690Z", - "updated_at": "2025-07-19T09:16:11.460690Z" - }, - { - "id": 209, - "name": "AquaPro Air Purifier 689J", - "description": "AquaPro Air Purifier 689J is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.26, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1256, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 50.4, - "mass": 1.6, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 49.6, - "mass": 1.1, - "unit": "kg" - } - ], - "components": [ - { - "id": 2091, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2092, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2093, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 209, - "url": "https://via.placeholder.com/400x300/0d732a/ffffff?text=AquaPro+Air+Purifier+689J", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-06-10T09:16:11.460721Z", - "updated_at": "2025-06-13T09:16:11.460721Z" - }, - { - "id": 210, - "name": "NeoCook Iron 975Q", - "description": "NeoCook Iron 975Q is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.73, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1365, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 43.2, - "mass": 1.67, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 38.6, - "mass": 1.03, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 18.2, - "mass": 0.34, - "unit": "kg" - } - ], - "components": [ - { - "id": 2101, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 2102, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 2103, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 2104, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 210, - "url": "https://via.placeholder.com/400x300/0ecacf/ffffff?text=NeoCook+Iron+975Q", - "description": "Iron product image" - } - ], - "created_at": "2024-03-26T09:16:11.460775Z", - "updated_at": "2025-05-10T09:16:11.460775Z" - }, - { - "id": 211, - "name": "ChefMate Rice Cooker 688A", - "description": "ChefMate Rice Cooker 688A is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.6, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1670, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 45.0, - "mass": 0.95, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 55.0, - "mass": 1.27, - "unit": "kg" - } - ], - "components": [ - { - "id": 2111, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2112, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2113, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2114, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2115, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 211, - "url": "https://via.placeholder.com/400x300/0905d7/ffffff?text=ChefMate+Rice+Cooker+688A", - "description": "Rice Cooker product image" - } - ], - "created_at": "2025-02-26T09:16:11.460815Z", - "updated_at": "2025-06-30T09:16:11.460815Z" - }, - { - "id": 212, - "name": "CleanWave Toaster 324U", - "description": "CleanWave Toaster 324U is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.32, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1310, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 21.8, - "mass": 0.81, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 22.9, - "mass": 0.51, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 27.9, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 27.4, - "mass": 0.8, - "unit": "kg" - } - ], - "components": [ - { - "id": 2121, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2122, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 212, - "url": "https://via.placeholder.com/400x300/01b496/ffffff?text=CleanWave+Toaster+324U", - "description": "Toaster product image" - } - ], - "created_at": "2024-12-30T09:16:11.460855Z", - "updated_at": "2025-06-12T09:16:11.460855Z" - }, - { - "id": 213, - "name": "CleanWave Toaster 334M", - "description": "CleanWave Toaster 334M is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.89, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 929, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 51.7, - "mass": 1.52, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 14.7, - "mass": 0.56, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 33.6, - "mass": 0.78, - "unit": "kg" - } - ], - "components": [ - { - "id": 2131, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2132, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 213, - "url": "https://via.placeholder.com/400x300/0e9982/ffffff?text=CleanWave+Toaster+334M", - "description": "Toaster product image" - } - ], - "created_at": "2025-01-21T09:16:11.460888Z", - "updated_at": "2025-06-26T09:16:11.460888Z" - }, - { - "id": 214, - "name": "AquaPro Monitor 790C", - "description": "AquaPro Monitor 790C is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.25, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 818, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 18.6, - "mass": 0.57, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 43.4, - "mass": 1.49, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 38.1, - "mass": 0.53, - "unit": "kg" - } - ], - "components": [ - { - "id": 2141, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2142, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2143, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2144, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2145, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 214, - "url": "https://via.placeholder.com/400x300/0c2322/ffffff?text=AquaPro+Monitor+790C", - "description": "Monitor product image" - } - ], - "created_at": "2024-12-09T09:16:11.460938Z", - "updated_at": "2025-05-03T09:16:11.460938Z" - }, - { - "id": 215, - "name": "ChefMate Monitor 826P", - "description": "ChefMate Monitor 826P is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.93, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1241, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 17.0, - "mass": 0.34, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 34.1, - "mass": 0.95, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 31.1, - "mass": 0.62, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 17.8, - "mass": 0.35, - "unit": "kg" - } - ], - "components": [ - { - "id": 2151, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2152, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2153, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2154, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 215, - "url": "https://via.placeholder.com/400x300/0b2990/ffffff?text=ChefMate+Monitor+826P", - "description": "Monitor product image" - } - ], - "created_at": "2024-04-29T09:16:11.460976Z", - "updated_at": "2025-05-13T09:16:11.460976Z" - }, - { - "id": 216, - "name": "AquaPro Blender 614W", - "description": "AquaPro Blender 614W is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.14, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2046, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 56.8, - "mass": 1.11, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 14.8, - "mass": 0.21, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 28.4, - "mass": 0.31, - "unit": "kg" - } - ], - "components": [ - { - "id": 2161, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 2162, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 2163, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 216, - "url": "https://via.placeholder.com/400x300/027527/ffffff?text=AquaPro+Blender+614W", - "description": "Blender product image" - } - ], - "created_at": "2024-04-20T09:16:11.461014Z", - "updated_at": "2025-07-22T09:16:11.461014Z" - }, - { - "id": 217, - "name": "AquaPro Blender 132T", - "description": "AquaPro Blender 132T is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.5, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1108, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 11.6, - "mass": 0.41, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 69.8, - "mass": 1.32, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 18.6, - "mass": 0.35, - "unit": "kg" - } - ], - "components": [ - { - "id": 2171, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 2172, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 2173, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 2174, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 217, - "url": "https://via.placeholder.com/400x300/078f15/ffffff?text=AquaPro+Blender+132T", - "description": "Blender product image" - } - ], - "created_at": "2024-05-03T09:16:11.461050Z", - "updated_at": "2025-06-26T09:16:11.461050Z" - }, - { - "id": 218, - "name": "CleanWave Humidifier 617O", - "description": "CleanWave Humidifier 617O is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.71, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1314, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 33.3, - "mass": 1.22, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 66.7, - "mass": 1.88, - "unit": "kg" - } - ], - "components": [ - { - "id": 2181, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2182, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 218, - "url": "https://via.placeholder.com/400x300/09da1c/ffffff?text=CleanWave+Humidifier+617O", - "description": "Humidifier product image" - } - ], - "created_at": "2024-08-26T09:16:11.461081Z", - "updated_at": "2025-07-07T09:16:11.461081Z" - }, - { - "id": 219, - "name": "EcoTech Kettle 628E", - "description": "EcoTech Kettle 628E is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.47, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2187, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 47.0, - "mass": 0.58, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 53.0, - "mass": 1.45, - "unit": "kg" - } - ], - "components": [ - { - "id": 2191, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2192, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 219, - "url": "https://via.placeholder.com/400x300/0dbff8/ffffff?text=EcoTech+Kettle+628E", - "description": "Kettle product image" - } - ], - "created_at": "2024-04-08T09:16:11.461116Z", - "updated_at": "2025-06-16T09:16:11.461116Z" - }, - { - "id": 220, - "name": "ZenGear Coffee Maker 413T", - "description": "ZenGear Coffee Maker 413T is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.28, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1659, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 11.0, - "mass": 0.27, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 47.3, - "mass": 0.58, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 41.8, - "mass": 1.18, - "unit": "kg" - } - ], - "components": [ - { - "id": 2201, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2202, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2203, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2204, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 220, - "url": "https://via.placeholder.com/400x300/046a5b/ffffff?text=ZenGear+Coffee+Maker+413T", - "description": "Coffee Maker product image" - } - ], - "created_at": "2025-03-19T09:16:11.461152Z", - "updated_at": "2025-06-04T09:16:11.461152Z" - }, - { - "id": 221, - "name": "PureLife Fan 491R", - "description": "PureLife Fan 491R is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.17, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 984, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 41.3, - "mass": 0.72, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 58.7, - "mass": 1.07, - "unit": "kg" - } - ], - "components": [ - { - "id": 2211, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 2212, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 2213, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 221, - "url": "https://via.placeholder.com/400x300/08557e/ffffff?text=PureLife+Fan+491R", - "description": "Fan product image" - } - ], - "created_at": "2024-05-05T09:16:11.461194Z", - "updated_at": "2025-04-29T09:16:11.461194Z" - }, - { - "id": 222, - "name": "SmartHome Fan 715N", - "description": "SmartHome Fan 715N is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.49, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2136, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 25.0, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 34.5, - "mass": 0.95, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 40.5, - "mass": 1.16, - "unit": "kg" - } - ], - "components": [ - { - "id": 2221, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 2222, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 2223, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 2224, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 222, - "url": "https://via.placeholder.com/400x300/05e641/ffffff?text=SmartHome+Fan+715N", - "description": "Fan product image" - } - ], - "created_at": "2025-02-19T09:16:11.461261Z", - "updated_at": "2025-07-06T09:16:11.461261Z" - }, - { - "id": 223, - "name": "NeoCook Toaster 809G", - "description": "NeoCook Toaster 809G is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.76, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1541, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 46.5, - "mass": 1.35, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 29.8, - "mass": 0.51, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 23.7, - "mass": 0.5, - "unit": "kg" - } - ], - "components": [ - { - "id": 2231, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2232, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2233, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 223, - "url": "https://via.placeholder.com/400x300/07ab6a/ffffff?text=NeoCook+Toaster+809G", - "description": "Toaster product image" - } - ], - "created_at": "2025-02-06T09:16:11.461307Z", - "updated_at": "2025-07-19T09:16:11.461307Z" - }, - { - "id": 224, - "name": "ChefMate Iron 970X", - "description": "ChefMate Iron 970X is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.46, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1848, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 30.8, - "mass": 0.4, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 36.4, - "mass": 0.77, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 32.9, - "mass": 0.47, - "unit": "kg" - } - ], - "components": [ - { - "id": 2241, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 2242, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 2243, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 2244, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 224, - "url": "https://via.placeholder.com/400x300/0763bf/ffffff?text=ChefMate+Iron+970X", - "description": "Iron product image" - } - ], - "created_at": "2024-08-13T09:16:11.461369Z", - "updated_at": "2025-05-22T09:16:11.461369Z" - }, - { - "id": 225, - "name": "AquaPro Humidifier 893T", - "description": "AquaPro Humidifier 893T is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.48, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1083, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 50.7, - "mass": 1.27, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 49.3, - "mass": 1.2, - "unit": "kg" - } - ], - "components": [ - { - "id": 2251, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2252, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2253, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2254, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 225, - "url": "https://via.placeholder.com/400x300/03c5d6/ffffff?text=AquaPro+Humidifier+893T", - "description": "Humidifier product image" - } - ], - "created_at": "2024-10-24T09:16:11.461433Z", - "updated_at": "2025-05-15T09:16:11.461433Z" - }, - { - "id": 226, - "name": "ZenGear Fan 548F", - "description": "ZenGear Fan 548F is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.61, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1765, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 63.1, - "mass": 1.46, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 36.9, - "mass": 0.95, - "unit": "kg" - } - ], - "components": [ - { - "id": 2261, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 2262, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 226, - "url": "https://via.placeholder.com/400x300/0b33a5/ffffff?text=ZenGear+Fan+548F", - "description": "Fan product image" - } - ], - "created_at": "2025-01-14T09:16:11.461481Z", - "updated_at": "2025-07-16T09:16:11.461481Z" - }, - { - "id": 227, - "name": "CleanWave Blender 976X", - "description": "CleanWave Blender 976X is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.65, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1615, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 25.0, - "mass": 0.56, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 55.6, - "mass": 1.81, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 19.4, - "mass": 0.28, - "unit": "kg" - } - ], - "components": [ - { - "id": 2271, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 2272, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 2273, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 2274, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 227, - "url": "https://via.placeholder.com/400x300/038d5b/ffffff?text=CleanWave+Blender+976X", - "description": "Blender product image" - } - ], - "created_at": "2024-08-29T09:16:11.461538Z", - "updated_at": "2025-05-31T09:16:11.461538Z" - }, - { - "id": 228, - "name": "PureLife Toaster 790V", - "description": "PureLife Toaster 790V is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.87, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1825, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 34.7, - "mass": 1.24, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 19.4, - "mass": 0.33, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 20.1, - "mass": 0.34, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 25.7, - "mass": 0.56, - "unit": "kg" - } - ], - "components": [ - { - "id": 2281, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2282, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2283, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2284, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 228, - "url": "https://via.placeholder.com/400x300/0a57db/ffffff?text=PureLife+Toaster+790V", - "description": "Toaster product image" - } - ], - "created_at": "2024-09-21T09:16:11.461585Z", - "updated_at": "2025-05-04T09:16:11.461585Z" - }, - { - "id": 229, - "name": "CleanWave Humidifier 848K", - "description": "CleanWave Humidifier 848K is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.17, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1439, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 27.4, - "mass": 0.49, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 28.4, - "mass": 0.79, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 16.3, - "mass": 0.5, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 27.9, - "mass": 0.74, - "unit": "kg" - } - ], - "components": [ - { - "id": 2291, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2292, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 229, - "url": "https://via.placeholder.com/400x300/0cc646/ffffff?text=CleanWave+Humidifier+848K", - "description": "Humidifier product image" - } - ], - "created_at": "2024-09-29T09:16:11.461627Z", - "updated_at": "2025-04-27T09:16:11.461627Z" - }, - { - "id": 230, - "name": "ZenGear Humidifier 675S", - "description": "ZenGear Humidifier 675S is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.24, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1324, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 65.1, - "mass": 2.23, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 34.9, - "mass": 0.62, - "unit": "kg" - } - ], - "components": [ - { - "id": 2301, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2302, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2303, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 230, - "url": "https://via.placeholder.com/400x300/03cc91/ffffff?text=ZenGear+Humidifier+675S", - "description": "Humidifier product image" - } - ], - "created_at": "2025-01-10T09:16:11.461666Z", - "updated_at": "2025-07-03T09:16:11.461666Z" - }, - { - "id": 231, - "name": "CleanWave Fan 815Z", - "description": "CleanWave Fan 815Z is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.49, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 994, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 51.2, - "mass": 1.62, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 48.8, - "mass": 0.6, - "unit": "kg" - } - ], - "components": [ - { - "id": 2311, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 2312, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 2313, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 231, - "url": "https://via.placeholder.com/400x300/0ab275/ffffff?text=CleanWave+Fan+815Z", - "description": "Fan product image" - } - ], - "created_at": "2024-11-04T09:16:11.462805Z", - "updated_at": "2025-07-16T09:16:11.462805Z" - }, - { - "id": 232, - "name": "ZenGear Humidifier 843P", - "description": "ZenGear Humidifier 843P is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.24, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1959, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 45.5, - "mass": 1.45, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 28.8, - "mass": 0.65, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 25.8, - "mass": 0.51, - "unit": "kg" - } - ], - "components": [ - { - "id": 2321, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2322, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 232, - "url": "https://via.placeholder.com/400x300/01ccb7/ffffff?text=ZenGear+Humidifier+843P", - "description": "Humidifier product image" - } - ], - "created_at": "2024-08-26T09:16:11.462873Z", - "updated_at": "2025-07-07T09:16:11.462873Z" - }, - { - "id": 233, - "name": "PureLife Coffee Maker 108Z", - "description": "PureLife Coffee Maker 108Z is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.6, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2114, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 21.3, - "mass": 0.31, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 34.9, - "mass": 0.45, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 33.1, - "mass": 0.67, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 10.7, - "mass": 0.19, - "unit": "kg" - } - ], - "components": [ - { - "id": 2331, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2332, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2333, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 233, - "url": "https://via.placeholder.com/400x300/09cc27/ffffff?text=PureLife+Coffee+Maker+108Z", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-08-10T09:16:11.462925Z", - "updated_at": "2025-06-01T09:16:11.462925Z" - }, - { - "id": 234, - "name": "CleanWave Toaster 110Y", - "description": "CleanWave Toaster 110Y is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.43, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1805, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 41.7, - "mass": 0.45, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 58.3, - "mass": 1.83, - "unit": "kg" - } - ], - "components": [ - { - "id": 2341, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2342, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2343, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 234, - "url": "https://via.placeholder.com/400x300/0d484f/ffffff?text=CleanWave+Toaster+110Y", - "description": "Toaster product image" - } - ], - "created_at": "2025-03-05T09:16:11.462964Z", - "updated_at": "2025-06-17T09:16:11.462964Z" - }, - { - "id": 235, - "name": "ZenGear Coffee Maker 902S", - "description": "ZenGear Coffee Maker 902S is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.44, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 849, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 45.2, - "mass": 1.62, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 54.8, - "mass": 0.8, - "unit": "kg" - } - ], - "components": [ - { - "id": 2351, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2352, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 235, - "url": "https://via.placeholder.com/400x300/0a5032/ffffff?text=ZenGear+Coffee+Maker+902S", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-06-30T09:16:11.462994Z", - "updated_at": "2025-06-09T09:16:11.462994Z" - }, - { - "id": 236, - "name": "CleanWave Coffee Maker 399S", - "description": "CleanWave Coffee Maker 399S is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.28, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1225, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 32.3, - "mass": 0.86, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 25.9, - "mass": 0.36, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 10.1, - "mass": 0.29, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 31.6, - "mass": 0.92, - "unit": "kg" - } - ], - "components": [ - { - "id": 2361, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2362, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2363, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2364, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2365, - "name": "Coffee Maker Component 5", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 236, - "url": "https://via.placeholder.com/400x300/07e9a0/ffffff?text=CleanWave+Coffee+Maker+399S", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-05-17T09:16:11.463033Z", - "updated_at": "2025-07-16T09:16:11.463033Z" - }, - { - "id": 237, - "name": "AquaPro Fan 303A", - "description": "AquaPro Fan 303A is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.93, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 35.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1400, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 31.8, - "mass": 0.51, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 37.1, - "mass": 0.62, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 31.1, - "mass": 1.15, - "unit": "kg" - } - ], - "components": [ - { - "id": 2371, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 2372, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 237, - "url": "https://via.placeholder.com/400x300/0758b4/ffffff?text=AquaPro+Fan+303A", - "description": "Fan product image" - } - ], - "created_at": "2024-12-07T09:16:11.463081Z", - "updated_at": "2025-05-16T09:16:11.463081Z" - }, - { - "id": 238, - "name": "CleanWave Monitor 457V", - "description": "CleanWave Monitor 457V is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.99, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 908, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 28.5, - "mass": 0.76, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 15.2, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 24.1, - "mass": 0.96, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 32.3, - "mass": 1.09, - "unit": "kg" - } - ], - "components": [ - { - "id": 2381, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2382, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2383, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2384, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 238, - "url": "https://via.placeholder.com/400x300/0cf971/ffffff?text=CleanWave+Monitor+457V", - "description": "Monitor product image" - } - ], - "created_at": "2024-04-08T09:16:11.463125Z", - "updated_at": "2025-06-29T09:16:11.463125Z" - }, - { - "id": 239, - "name": "NeoCook Fan 246N", - "description": "NeoCook Fan 246N is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.05, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1852, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 56.9, - "mass": 0.59, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 43.1, - "mass": 0.76, - "unit": "kg" - } - ], - "components": [ - { - "id": 2391, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 2392, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 2393, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 2394, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 239, - "url": "https://via.placeholder.com/400x300/08ebf2/ffffff?text=NeoCook+Fan+246N", - "description": "Fan product image" - } - ], - "created_at": "2024-07-13T09:16:11.463381Z", - "updated_at": "2025-06-16T09:16:11.463381Z" - }, - { - "id": 240, - "name": "CleanWave Monitor 716X", - "description": "CleanWave Monitor 716X is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.51, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 897, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 29.0, - "mass": 0.79, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 33.9, - "mass": 1.25, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 37.1, - "mass": 0.95, - "unit": "kg" - } - ], - "components": [ - { - "id": 2401, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2402, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2403, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2404, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2405, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 240, - "url": "https://via.placeholder.com/400x300/0ba5c3/ffffff?text=CleanWave+Monitor+716X", - "description": "Monitor product image" - } - ], - "created_at": "2024-05-21T09:16:11.463459Z", - "updated_at": "2025-04-25T09:16:11.463459Z" - }, - { - "id": 241, - "name": "ZenGear Coffee Maker 829T", - "description": "ZenGear Coffee Maker 829T is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.9, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1990, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 27.0, - "mass": 0.73, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 11.3, - "mass": 0.17, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 30.5, - "mass": 0.32, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 31.2, - "mass": 1.08, - "unit": "kg" - } - ], - "components": [ - { - "id": 2411, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2412, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2413, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2414, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2415, - "name": "Coffee Maker Component 5", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 241, - "url": "https://via.placeholder.com/400x300/02931e/ffffff?text=ZenGear+Coffee+Maker+829T", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-09-17T09:16:11.463501Z", - "updated_at": "2025-04-29T09:16:11.463501Z" - }, - { - "id": 242, - "name": "SmartHome Coffee Maker 492Z", - "description": "SmartHome Coffee Maker 492Z is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.89, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2025, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 36.4, - "mass": 1.37, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 10.9, - "mass": 0.35, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 13.2, - "mass": 0.21, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 39.5, - "mass": 1.56, - "unit": "kg" - } - ], - "components": [ - { - "id": 2421, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2422, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2423, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2424, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2425, - "name": "Coffee Maker Component 5", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 242, - "url": "https://via.placeholder.com/400x300/0db02f/ffffff?text=SmartHome+Coffee+Maker+492Z", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-10-18T09:16:11.463557Z", - "updated_at": "2025-06-13T09:16:11.463557Z" - }, - { - "id": 243, - "name": "CleanWave Iron 762S", - "description": "CleanWave Iron 762S is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.72, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 912, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 60.0, - "mass": 1.3, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 40.0, - "mass": 0.87, - "unit": "kg" - } - ], - "components": [ - { - "id": 2431, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 2432, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 2433, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 2434, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - }, - { - "id": 2435, - "name": "Iron Component 5", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 243, - "url": "https://via.placeholder.com/400x300/0c3636/ffffff?text=CleanWave+Iron+762S", - "description": "Iron product image" - } - ], - "created_at": "2025-04-09T09:16:11.463601Z", - "updated_at": "2025-06-21T09:16:11.463601Z" - }, - { - "id": 244, - "name": "ZenGear Blender 900X", - "description": "ZenGear Blender 900X is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.21, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2149, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 32.6, - "mass": 0.4, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 40.7, - "mass": 1.28, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 26.7, - "mass": 0.74, - "unit": "kg" - } - ], - "components": [ - { - "id": 2441, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 2442, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 2443, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 2444, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 244, - "url": "https://via.placeholder.com/400x300/0ac2f2/ffffff?text=ZenGear+Blender+900X", - "description": "Blender product image" - } - ], - "created_at": "2024-11-23T09:16:11.463632Z", - "updated_at": "2025-07-25T09:16:11.463632Z" - }, - { - "id": 245, - "name": "CleanWave Rice Cooker 166O", - "description": "CleanWave Rice Cooker 166O is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.85, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1670, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 32.8, - "mass": 0.79, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 67.2, - "mass": 0.8, - "unit": "kg" - } - ], - "components": [ - { - "id": 2451, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2452, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2453, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2454, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2455, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 245, - "url": "https://via.placeholder.com/400x300/097866/ffffff?text=CleanWave+Rice+Cooker+166O", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-04-17T09:16:11.463662Z", - "updated_at": "2025-07-13T09:16:11.463662Z" - }, - { - "id": 246, - "name": "AquaPro Air Purifier 638V", - "description": "AquaPro Air Purifier 638V is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.07, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1080, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 14.3, - "mass": 0.24, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 19.0, - "mass": 0.54, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 26.5, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 40.1, - "mass": 0.63, - "unit": "kg" - } - ], - "components": [ - { - "id": 2461, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2462, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2463, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2464, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2465, - "name": "Air Purifier Component 5", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 246, - "url": "https://via.placeholder.com/400x300/0a3080/ffffff?text=AquaPro+Air+Purifier+638V", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-08-03T09:16:11.463706Z", - "updated_at": "2025-06-08T09:16:11.463706Z" - }, - { - "id": 247, - "name": "CleanWave Kettle 403Y", - "description": "CleanWave Kettle 403Y is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.67, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1151, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 23.3, - "mass": 0.68, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 17.4, - "mass": 0.52, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 59.3, - "mass": 1.72, - "unit": "kg" - } - ], - "components": [ - { - "id": 2471, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2472, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 247, - "url": "https://via.placeholder.com/400x300/0dbac5/ffffff?text=CleanWave+Kettle+403Y", - "description": "Kettle product image" - } - ], - "created_at": "2024-05-19T09:16:11.463762Z", - "updated_at": "2025-06-30T09:16:11.463762Z" - }, - { - "id": 248, - "name": "CleanWave Coffee Maker 397Y", - "description": "CleanWave Coffee Maker 397Y is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.16, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1273, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 41.0, - "mass": 1.04, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 12.2, - "mass": 0.35, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 39.6, - "mass": 1.18, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 7.2, - "mass": 0.2, - "unit": "kg" - } - ], - "components": [ - { - "id": 2481, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2482, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2483, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2484, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 248, - "url": "https://via.placeholder.com/400x300/0c9a01/ffffff?text=CleanWave+Coffee+Maker+397Y", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-06-20T09:16:11.463809Z", - "updated_at": "2025-05-25T09:16:11.463809Z" - }, - { - "id": 249, - "name": "CleanWave Monitor 619K", - "description": "CleanWave Monitor 619K is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.55, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1101, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 34.8, - "mass": 1.17, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 65.2, - "mass": 1.74, - "unit": "kg" - } - ], - "components": [ - { - "id": 2491, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2492, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2493, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2494, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2495, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 249, - "url": "https://via.placeholder.com/400x300/0a3344/ffffff?text=CleanWave+Monitor+619K", - "description": "Monitor product image" - } - ], - "created_at": "2025-01-28T09:16:11.463855Z", - "updated_at": "2025-07-14T09:16:11.463855Z" - }, - { - "id": 250, - "name": "AquaPro Air Purifier 504C", - "description": "AquaPro Air Purifier 504C is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.94, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1246, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 29.0, - "mass": 0.35, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 28.0, - "mass": 0.63, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 43.0, - "mass": 1.0, - "unit": "kg" - } - ], - "components": [ - { - "id": 2501, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2502, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2503, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 250, - "url": "https://via.placeholder.com/400x300/052d6b/ffffff?text=AquaPro+Air+Purifier+504C", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-12-19T09:16:11.463920Z", - "updated_at": "2025-05-24T09:16:11.463920Z" - }, - { - "id": 251, - "name": "SmartHome Monitor 713U", - "description": "SmartHome Monitor 713U is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.9, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1438, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 28.6, - "mass": 0.68, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 27.1, - "mass": 0.67, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 44.4, - "mass": 1.5, - "unit": "kg" - } - ], - "components": [ - { - "id": 2511, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2512, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 251, - "url": "https://via.placeholder.com/400x300/068125/ffffff?text=SmartHome+Monitor+713U", - "description": "Monitor product image" - } - ], - "created_at": "2025-02-22T09:16:11.463968Z", - "updated_at": "2025-07-11T09:16:11.463968Z" - }, - { - "id": 252, - "name": "SmartHome Humidifier 873Q", - "description": "SmartHome Humidifier 873Q is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.41, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1562, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 27.5, - "mass": 0.39, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 19.8, - "mass": 0.72, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 52.7, - "mass": 1.26, - "unit": "kg" - } - ], - "components": [ - { - "id": 2521, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2522, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 252, - "url": "https://via.placeholder.com/400x300/0a4e8a/ffffff?text=SmartHome+Humidifier+873Q", - "description": "Humidifier product image" - } - ], - "created_at": "2024-10-31T09:16:11.464031Z", - "updated_at": "2025-07-03T09:16:11.464031Z" - }, - { - "id": 253, - "name": "EcoTech Fan 327S", - "description": "EcoTech Fan 327S is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.66, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1386, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 15.0, - "mass": 0.4, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 39.8, - "mass": 1.46, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 45.1, - "mass": 1.22, - "unit": "kg" - } - ], - "components": [ - { - "id": 2531, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 2532, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 2533, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 2534, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 253, - "url": "https://via.placeholder.com/400x300/0625c2/ffffff?text=EcoTech+Fan+327S", - "description": "Fan product image" - } - ], - "created_at": "2024-03-30T09:16:11.464084Z", - "updated_at": "2025-07-26T09:16:11.464084Z" - }, - { - "id": 254, - "name": "SmartHome Iron 836G", - "description": "SmartHome Iron 836G is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.72, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1489, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 23.3, - "mass": 0.7, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 76.7, - "mass": 1.06, - "unit": "kg" - } - ], - "components": [ - { - "id": 2541, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 2542, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 2543, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 2544, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 254, - "url": "https://via.placeholder.com/400x300/0f190b/ffffff?text=SmartHome+Iron+836G", - "description": "Iron product image" - } - ], - "created_at": "2025-03-27T09:16:11.464144Z", - "updated_at": "2025-05-15T09:16:11.464144Z" - }, - { - "id": 255, - "name": "EcoTech Rice Cooker 371T", - "description": "EcoTech Rice Cooker 371T is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.77, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 912, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 16.0, - "mass": 0.39, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 28.3, - "mass": 0.47, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 55.7, - "mass": 1.23, - "unit": "kg" - } - ], - "components": [ - { - "id": 2551, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2552, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2553, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 255, - "url": "https://via.placeholder.com/400x300/0dbca9/ffffff?text=EcoTech+Rice+Cooker+371T", - "description": "Rice Cooker product image" - } - ], - "created_at": "2025-01-01T09:16:11.464212Z", - "updated_at": "2025-05-09T09:16:11.464212Z" - }, - { - "id": 256, - "name": "SmartHome Rice Cooker 865X", - "description": "SmartHome Rice Cooker 865X is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.02, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 913, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 35.7, - "mass": 0.49, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 36.4, - "mass": 0.68, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 27.9, - "mass": 1.04, - "unit": "kg" - } - ], - "components": [ - { - "id": 2561, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2562, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2563, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 256, - "url": "https://via.placeholder.com/400x300/036c99/ffffff?text=SmartHome+Rice+Cooker+865X", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-03-23T09:16:11.464310Z", - "updated_at": "2025-05-10T09:16:11.464310Z" - }, - { - "id": 257, - "name": "PureLife Kettle 821Z", - "description": "PureLife Kettle 821Z is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.93, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1831, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 12.6, - "mass": 0.28, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 40.9, - "mass": 0.88, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 46.5, - "mass": 1.83, - "unit": "kg" - } - ], - "components": [ - { - "id": 2571, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2572, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2573, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2574, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 257, - "url": "https://via.placeholder.com/400x300/023c5a/ffffff?text=PureLife+Kettle+821Z", - "description": "Kettle product image" - } - ], - "created_at": "2024-08-18T09:16:11.464367Z", - "updated_at": "2025-06-14T09:16:11.464367Z" - }, - { - "id": 258, - "name": "EcoTech Rice Cooker 869X", - "description": "EcoTech Rice Cooker 869X is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.09, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 928, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 35.4, - "mass": 1.14, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 27.8, - "mass": 1.01, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 36.8, - "mass": 1.28, - "unit": "kg" - } - ], - "components": [ - { - "id": 2581, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2582, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 258, - "url": "https://via.placeholder.com/400x300/0b7f3c/ffffff?text=EcoTech+Rice+Cooker+869X", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-09-22T09:16:11.464435Z", - "updated_at": "2025-06-30T09:16:11.464435Z" - }, - { - "id": 259, - "name": "ZenGear Humidifier 787T", - "description": "ZenGear Humidifier 787T is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.09, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2044, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 31.7, - "mass": 0.59, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 33.3, - "mass": 0.43, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 20.8, - "mass": 0.77, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 14.2, - "mass": 0.54, - "unit": "kg" - } - ], - "components": [ - { - "id": 2591, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2592, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 259, - "url": "https://via.placeholder.com/400x300/0f1fa5/ffffff?text=ZenGear+Humidifier+787T", - "description": "Humidifier product image" - } - ], - "created_at": "2024-08-25T09:16:11.464493Z", - "updated_at": "2025-05-03T09:16:11.464493Z" - }, - { - "id": 260, - "name": "EcoTech Kettle 165T", - "description": "EcoTech Kettle 165T is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.99, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1532, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 54.2, - "mass": 0.55, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 45.8, - "mass": 0.82, - "unit": "kg" - } - ], - "components": [ - { - "id": 2601, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2602, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2603, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2604, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2605, - "name": "Kettle Component 5", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 260, - "url": "https://via.placeholder.com/400x300/080cf2/ffffff?text=EcoTech+Kettle+165T", - "description": "Kettle product image" - } - ], - "created_at": "2024-06-28T09:16:11.464549Z", - "updated_at": "2025-05-16T09:16:11.464549Z" - }, - { - "id": 261, - "name": "NeoCook Coffee Maker 295E", - "description": "NeoCook Coffee Maker 295E is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.1, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1318, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 40.2, - "mass": 0.43, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 59.8, - "mass": 1.97, - "unit": "kg" - } - ], - "components": [ - { - "id": 2611, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2612, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2613, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 261, - "url": "https://via.placeholder.com/400x300/07d5e7/ffffff?text=NeoCook+Coffee+Maker+295E", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-11-26T09:16:11.464610Z", - "updated_at": "2025-05-06T09:16:11.464610Z" - }, - { - "id": 262, - "name": "AquaPro Blender 981U", - "description": "AquaPro Blender 981U is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.93, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1731, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 30.7, - "mass": 1.23, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 59.4, - "mass": 1.63, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 9.9, - "mass": 0.12, - "unit": "kg" - } - ], - "components": [ - { - "id": 2621, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 2622, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 2623, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 2624, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 2625, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 262, - "url": "https://via.placeholder.com/400x300/091f8c/ffffff?text=AquaPro+Blender+981U", - "description": "Blender product image" - } - ], - "created_at": "2024-03-26T09:16:11.464678Z", - "updated_at": "2025-05-06T09:16:11.464678Z" - }, - { - "id": 263, - "name": "EcoTech Monitor 313N", - "description": "EcoTech Monitor 313N is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.8, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1380, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 78.3, - "mass": 0.8, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 21.7, - "mass": 0.67, - "unit": "kg" - } - ], - "components": [ - { - "id": 2631, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2632, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2633, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 263, - "url": "https://via.placeholder.com/400x300/0cacdf/ffffff?text=EcoTech+Monitor+313N", - "description": "Monitor product image" - } - ], - "created_at": "2024-10-02T09:16:11.464733Z", - "updated_at": "2025-06-11T09:16:11.464733Z" - }, - { - "id": 264, - "name": "PureLife Monitor 694H", - "description": "PureLife Monitor 694H is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.12, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1466, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 44.4, - "mass": 1.66, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 55.6, - "mass": 2.1, - "unit": "kg" - } - ], - "components": [ - { - "id": 2641, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2642, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 264, - "url": "https://via.placeholder.com/400x300/05f976/ffffff?text=PureLife+Monitor+694H", - "description": "Monitor product image" - } - ], - "created_at": "2024-10-22T09:16:11.464781Z", - "updated_at": "2025-07-09T09:16:11.464781Z" - }, - { - "id": 265, - "name": "PureLife Toaster 748C", - "description": "PureLife Toaster 748C is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.23, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1253, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 19.8, - "mass": 0.3, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 28.4, - "mass": 0.69, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 21.6, - "mass": 0.56, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 30.2, - "mass": 0.49, - "unit": "kg" - } - ], - "components": [ - { - "id": 2651, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2652, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2653, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2654, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2655, - "name": "Toaster Component 5", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 265, - "url": "https://via.placeholder.com/400x300/0d721e/ffffff?text=PureLife+Toaster+748C", - "description": "Toaster product image" - } - ], - "created_at": "2024-03-27T09:16:11.464862Z", - "updated_at": "2025-05-08T09:16:11.464862Z" - }, - { - "id": 266, - "name": "PureLife Humidifier 160T", - "description": "PureLife Humidifier 160T is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.21, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2032, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 76.5, - "mass": 1.65, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 23.5, - "mass": 0.41, - "unit": "kg" - } - ], - "components": [ - { - "id": 2661, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2662, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 266, - "url": "https://via.placeholder.com/400x300/05145a/ffffff?text=PureLife+Humidifier+160T", - "description": "Humidifier product image" - } - ], - "created_at": "2024-03-19T09:16:11.464975Z", - "updated_at": "2025-07-30T09:16:11.464975Z" - }, - { - "id": 267, - "name": "ChefMate Air Purifier 951O", - "description": "ChefMate Air Purifier 951O is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.94, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1994, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 11.0, - "mass": 0.34, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 39.0, - "mass": 0.53, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 32.2, - "mass": 1.24, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 17.8, - "mass": 0.42, - "unit": "kg" - } - ], - "components": [ - { - "id": 2671, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2672, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2673, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2674, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2675, - "name": "Air Purifier Component 5", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 267, - "url": "https://via.placeholder.com/400x300/095092/ffffff?text=ChefMate+Air+Purifier+951O", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-09-04T09:16:11.465048Z", - "updated_at": "2025-04-30T09:16:11.465048Z" - }, - { - "id": 268, - "name": "AquaPro Air Purifier 116R", - "description": "AquaPro Air Purifier 116R is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.43, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1660, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 50.0, - "mass": 1.22, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 50.0, - "mass": 1.5, - "unit": "kg" - } - ], - "components": [ - { - "id": 2681, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2682, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 268, - "url": "https://via.placeholder.com/400x300/058df3/ffffff?text=AquaPro+Air+Purifier+116R", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-12-31T09:16:11.465104Z", - "updated_at": "2025-06-17T09:16:11.465104Z" - }, - { - "id": 269, - "name": "ChefMate Toaster 145U", - "description": "ChefMate Toaster 145U is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.73, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1634, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 55.3, - "mass": 1.88, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 44.7, - "mass": 1.59, - "unit": "kg" - } - ], - "components": [ - { - "id": 2691, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2692, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 269, - "url": "https://via.placeholder.com/400x300/08ac73/ffffff?text=ChefMate+Toaster+145U", - "description": "Toaster product image" - } - ], - "created_at": "2024-07-02T09:16:11.465165Z", - "updated_at": "2025-06-18T09:16:11.465165Z" - }, - { - "id": 270, - "name": "CleanWave Rice Cooker 594W", - "description": "CleanWave Rice Cooker 594W is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.0, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 890, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 22.8, - "mass": 0.46, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 39.1, - "mass": 0.87, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 38.0, - "mass": 0.79, - "unit": "kg" - } - ], - "components": [ - { - "id": 2701, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2702, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2703, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 270, - "url": "https://via.placeholder.com/400x300/08ccc2/ffffff?text=CleanWave+Rice+Cooker+594W", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-09-30T09:16:11.465217Z", - "updated_at": "2025-07-18T09:16:11.465217Z" - }, - { - "id": 271, - "name": "NeoCook Coffee Maker 582N", - "description": "NeoCook Coffee Maker 582N is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.07, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1082, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 17.9, - "mass": 0.34, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 46.2, - "mass": 0.78, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 35.9, - "mass": 0.78, - "unit": "kg" - } - ], - "components": [ - { - "id": 2711, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2712, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 271, - "url": "https://via.placeholder.com/400x300/07ce5c/ffffff?text=NeoCook+Coffee+Maker+582N", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-11-03T09:16:11.465278Z", - "updated_at": "2025-06-03T09:16:11.465278Z" - }, - { - "id": 272, - "name": "EcoTech Blender 933K", - "description": "EcoTech Blender 933K is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.01, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1007, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 53.4, - "mass": 0.6, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 20.5, - "mass": 0.68, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 26.1, - "mass": 0.73, - "unit": "kg" - } - ], - "components": [ - { - "id": 2721, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 2722, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 2723, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 2724, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 272, - "url": "https://via.placeholder.com/400x300/0b3398/ffffff?text=EcoTech+Blender+933K", - "description": "Blender product image" - } - ], - "created_at": "2025-04-17T09:16:11.465341Z", - "updated_at": "2025-06-02T09:16:11.465341Z" - }, - { - "id": 273, - "name": "CleanWave Coffee Maker 371O", - "description": "CleanWave Coffee Maker 371O is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.21, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1380, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 43.4, - "mass": 0.47, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 27.7, - "mass": 0.63, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 28.9, - "mass": 0.64, - "unit": "kg" - } - ], - "components": [ - { - "id": 2731, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2732, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2733, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2734, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2735, - "name": "Coffee Maker Component 5", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 273, - "url": "https://via.placeholder.com/400x300/0aa952/ffffff?text=CleanWave+Coffee+Maker+371O", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-11-13T09:16:11.465394Z", - "updated_at": "2025-06-16T09:16:11.465394Z" - }, - { - "id": 274, - "name": "CleanWave Rice Cooker 295I", - "description": "CleanWave Rice Cooker 295I is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.48, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1775, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 12.9, - "mass": 0.41, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 41.9, - "mass": 1.47, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 45.2, - "mass": 1.69, - "unit": "kg" - } - ], - "components": [ - { - "id": 2741, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2742, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 274, - "url": "https://via.placeholder.com/400x300/0a3127/ffffff?text=CleanWave+Rice+Cooker+295I", - "description": "Rice Cooker product image" - } - ], - "created_at": "2025-02-11T09:16:11.465465Z", - "updated_at": "2025-06-06T09:16:11.465465Z" - }, - { - "id": 275, - "name": "CleanWave Air Purifier 656B", - "description": "CleanWave Air Purifier 656B is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.0, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1657, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 55.7, - "mass": 1.49, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 44.3, - "mass": 1.49, - "unit": "kg" - } - ], - "components": [ - { - "id": 2751, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2752, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2753, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2754, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 275, - "url": "https://via.placeholder.com/400x300/04e70f/ffffff?text=CleanWave+Air+Purifier+656B", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-05-07T09:16:11.465515Z", - "updated_at": "2025-06-24T09:16:11.465515Z" - }, - { - "id": 276, - "name": "EcoTech Coffee Maker 165P", - "description": "EcoTech Coffee Maker 165P is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.29, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1181, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 28.2, - "mass": 1.0, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 7.1, - "mass": 0.12, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 32.7, - "mass": 0.9, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 32.1, - "mass": 0.44, - "unit": "kg" - } - ], - "components": [ - { - "id": 2761, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2762, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2763, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2764, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2765, - "name": "Coffee Maker Component 5", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 276, - "url": "https://via.placeholder.com/400x300/0978e5/ffffff?text=EcoTech+Coffee+Maker+165P", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-08-27T09:16:11.465587Z", - "updated_at": "2025-04-26T09:16:11.465587Z" - }, - { - "id": 277, - "name": "CleanWave Fan 815K", - "description": "CleanWave Fan 815K is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.69, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1768, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 10.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 13.6, - "mass": 0.49, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 30.0, - "mass": 0.69, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 27.9, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 28.6, - "mass": 1.02, - "unit": "kg" - } - ], - "components": [ - { - "id": 2771, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 2772, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 2773, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 277, - "url": "https://via.placeholder.com/400x300/09935b/ffffff?text=CleanWave+Fan+815K", - "description": "Fan product image" - } - ], - "created_at": "2025-04-17T09:16:11.465651Z", - "updated_at": "2025-05-10T09:16:11.465651Z" - }, - { - "id": 278, - "name": "NeoCook Toaster 826V", - "description": "NeoCook Toaster 826V is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.36, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 986, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 11.5, - "mass": 0.29, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 35.4, - "mass": 0.62, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 42.3, - "mass": 1.13, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 10.8, - "mass": 0.13, - "unit": "kg" - } - ], - "components": [ - { - "id": 2781, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2782, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2783, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 278, - "url": "https://via.placeholder.com/400x300/0b3fe6/ffffff?text=NeoCook+Toaster+826V", - "description": "Toaster product image" - } - ], - "created_at": "2024-08-19T09:16:11.465712Z", - "updated_at": "2025-06-23T09:16:11.465712Z" - }, - { - "id": 279, - "name": "PureLife Monitor 317U", - "description": "PureLife Monitor 317U is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.86, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2167, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 27.3, - "mass": 0.6, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 36.4, - "mass": 1.05, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 10.7, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 25.6, - "mass": 0.77, - "unit": "kg" - } - ], - "components": [ - { - "id": 2791, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2792, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 279, - "url": "https://via.placeholder.com/400x300/0f07a9/ffffff?text=PureLife+Monitor+317U", - "description": "Monitor product image" - } - ], - "created_at": "2025-04-23T09:16:11.465768Z", - "updated_at": "2025-06-12T09:16:11.465768Z" - }, - { - "id": 280, - "name": "SmartHome Rice Cooker 990X", - "description": "SmartHome Rice Cooker 990X is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.81, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1068, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 44.7, - "mass": 1.58, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 55.3, - "mass": 0.61, - "unit": "kg" - } - ], - "components": [ - { - "id": 2801, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2802, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2803, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2804, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2805, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 280, - "url": "https://via.placeholder.com/400x300/0316f5/ffffff?text=SmartHome+Rice+Cooker+990X", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-12-02T09:16:11.465843Z", - "updated_at": "2025-06-13T09:16:11.465843Z" - }, - { - "id": 281, - "name": "ChefMate Humidifier 455V", - "description": "ChefMate Humidifier 455V is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.51, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1805, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 56.2, - "mass": 0.9, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 31.2, - "mass": 0.38, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 12.5, - "mass": 0.34, - "unit": "kg" - } - ], - "components": [ - { - "id": 2811, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2812, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2813, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2814, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 2815, - "name": "Humidifier Component 5", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 281, - "url": "https://via.placeholder.com/400x300/034834/ffffff?text=ChefMate+Humidifier+455V", - "description": "Humidifier product image" - } - ], - "created_at": "2025-04-08T09:16:11.465899Z", - "updated_at": "2025-06-16T09:16:11.465899Z" - }, - { - "id": 282, - "name": "NeoCook Monitor 125C", - "description": "NeoCook Monitor 125C is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.09, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1787, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 50.4, - "mass": 1.55, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 38.7, - "mass": 1.48, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 10.9, - "mass": 0.41, - "unit": "kg" - } - ], - "components": [ - { - "id": 2821, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2822, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 282, - "url": "https://via.placeholder.com/400x300/0c4961/ffffff?text=NeoCook+Monitor+125C", - "description": "Monitor product image" - } - ], - "created_at": "2024-12-20T09:16:11.466151Z", - "updated_at": "2025-05-23T09:16:11.466151Z" - }, - { - "id": 283, - "name": "EcoTech Iron 197P", - "description": "EcoTech Iron 197P is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.44, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2065, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 36.4, - "mass": 0.49, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 41.7, - "mass": 0.46, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 22.0, - "mass": 0.39, - "unit": "kg" - } - ], - "components": [ - { - "id": 2831, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 2832, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 2833, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 2834, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - }, - { - "id": 2835, - "name": "Iron Component 5", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 283, - "url": "https://via.placeholder.com/400x300/01b85e/ffffff?text=EcoTech+Iron+197P", - "description": "Iron product image" - } - ], - "created_at": "2025-04-19T09:16:11.466258Z", - "updated_at": "2025-07-28T09:16:11.466258Z" - }, - { - "id": 284, - "name": "CleanWave Monitor 381B", - "description": "CleanWave Monitor 381B is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.32, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1101, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 41.2, - "mass": 1.49, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 17.6, - "mass": 0.34, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 41.2, - "mass": 1.6, - "unit": "kg" - } - ], - "components": [ - { - "id": 2841, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2842, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2843, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2844, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2845, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 284, - "url": "https://via.placeholder.com/400x300/0dc916/ffffff?text=CleanWave+Monitor+381B", - "description": "Monitor product image" - } - ], - "created_at": "2025-02-22T09:16:11.466327Z", - "updated_at": "2025-07-06T09:16:11.466327Z" - }, - { - "id": 285, - "name": "EcoTech Monitor 150C", - "description": "EcoTech Monitor 150C is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.89, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1475, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 44.0, - "mass": 1.02, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 21.6, - "mass": 0.81, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 34.4, - "mass": 1.14, - "unit": "kg" - } - ], - "components": [ - { - "id": 2851, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2852, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2853, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2854, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 285, - "url": "https://via.placeholder.com/400x300/0bd853/ffffff?text=EcoTech+Monitor+150C", - "description": "Monitor product image" - } - ], - "created_at": "2024-07-26T09:16:11.466367Z", - "updated_at": "2025-07-05T09:16:11.466367Z" - }, - { - "id": 286, - "name": "AquaPro Monitor 255R", - "description": "AquaPro Monitor 255R is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.88, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1632, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 27.5, - "mass": 0.59, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 27.5, - "mass": 1.1, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 44.9, - "mass": 1.29, - "unit": "kg" - } - ], - "components": [ - { - "id": 2861, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2862, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2863, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 286, - "url": "https://via.placeholder.com/400x300/0309c4/ffffff?text=AquaPro+Monitor+255R", - "description": "Monitor product image" - } - ], - "created_at": "2025-02-16T09:16:11.466413Z", - "updated_at": "2025-07-24T09:16:11.466413Z" - }, - { - "id": 287, - "name": "ChefMate Monitor 552Y", - "description": "ChefMate Monitor 552Y is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.43, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1820, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 19.5, - "mass": 0.22, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 28.9, - "mass": 0.69, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 18.0, - "mass": 0.2, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 33.6, - "mass": 1.33, - "unit": "kg" - } - ], - "components": [ - { - "id": 2871, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2872, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2873, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2874, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2875, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 287, - "url": "https://via.placeholder.com/400x300/0e39d7/ffffff?text=ChefMate+Monitor+552Y", - "description": "Monitor product image" - } - ], - "created_at": "2024-05-08T09:16:11.466604Z", - "updated_at": "2025-07-26T09:16:11.466604Z" - }, - { - "id": 288, - "name": "EcoTech Monitor 479I", - "description": "EcoTech Monitor 479I is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.67, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1754, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 29.7, - "mass": 0.9, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 32.2, - "mass": 1.28, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 18.6, - "mass": 0.62, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 19.5, - "mass": 0.32, - "unit": "kg" - } - ], - "components": [ - { - "id": 2881, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2882, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2883, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2884, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2885, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 288, - "url": "https://via.placeholder.com/400x300/05e5d9/ffffff?text=EcoTech+Monitor+479I", - "description": "Monitor product image" - } - ], - "created_at": "2025-01-24T09:16:11.466670Z", - "updated_at": "2025-07-20T09:16:11.466670Z" - }, - { - "id": 289, - "name": "AquaPro Blender 305E", - "description": "AquaPro Blender 305E is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.0, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1115, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 24.8, - "mass": 0.96, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 55.0, - "mass": 1.2, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 20.2, - "mass": 0.76, - "unit": "kg" - } - ], - "components": [ - { - "id": 2891, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 2892, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 2893, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 2894, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 2895, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 289, - "url": "https://via.placeholder.com/400x300/01a1d2/ffffff?text=AquaPro+Blender+305E", - "description": "Blender product image" - } - ], - "created_at": "2024-12-27T09:16:11.466726Z", - "updated_at": "2025-06-27T09:16:11.466726Z" - }, - { - "id": 290, - "name": "AquaPro Monitor 506U", - "description": "AquaPro Monitor 506U is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.6, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1646, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 26.1, - "mass": 0.81, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 29.3, - "mass": 0.9, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 25.0, - "mass": 0.82, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 19.6, - "mass": 0.29, - "unit": "kg" - } - ], - "components": [ - { - "id": 2901, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2902, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2903, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 290, - "url": "https://via.placeholder.com/400x300/02fd13/ffffff?text=AquaPro+Monitor+506U", - "description": "Monitor product image" - } - ], - "created_at": "2025-02-11T09:16:11.466786Z", - "updated_at": "2025-04-26T09:16:11.466786Z" - }, - { - "id": 291, - "name": "PureLife Air Purifier 874X", - "description": "PureLife Air Purifier 874X is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.68, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1175, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 53.2, - "mass": 1.52, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 46.8, - "mass": 0.77, - "unit": "kg" - } - ], - "components": [ - { - "id": 2911, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 2912, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 291, - "url": "https://via.placeholder.com/400x300/0dc724/ffffff?text=PureLife+Air+Purifier+874X", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-05-23T09:16:11.466843Z", - "updated_at": "2025-06-19T09:16:11.466843Z" - }, - { - "id": 292, - "name": "SmartHome Blender 307I", - "description": "SmartHome Blender 307I is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.66, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1648, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 17.8, - "mass": 0.37, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 37.8, - "mass": 1.09, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 16.7, - "mass": 0.2, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 27.8, - "mass": 1.09, - "unit": "kg" - } - ], - "components": [ - { - "id": 2921, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 2922, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 292, - "url": "https://via.placeholder.com/400x300/05fb28/ffffff?text=SmartHome+Blender+307I", - "description": "Blender product image" - } - ], - "created_at": "2025-02-28T09:16:11.466936Z", - "updated_at": "2025-06-02T09:16:11.466936Z" - }, - { - "id": 293, - "name": "PureLife Coffee Maker 936G", - "description": "PureLife Coffee Maker 936G is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.33, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1181, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 51.7, - "mass": 1.13, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 48.3, - "mass": 0.78, - "unit": "kg" - } - ], - "components": [ - { - "id": 2931, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 2932, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 293, - "url": "https://via.placeholder.com/400x300/07d5d7/ffffff?text=PureLife+Coffee+Maker+936G", - "description": "Coffee Maker product image" - } - ], - "created_at": "2025-02-22T09:16:11.466985Z", - "updated_at": "2025-05-19T09:16:11.466985Z" - }, - { - "id": 294, - "name": "NeoCook Toaster 589A", - "description": "NeoCook Toaster 589A is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.64, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1699, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 39.1, - "mass": 0.73, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 26.3, - "mass": 0.69, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 34.6, - "mass": 1.04, - "unit": "kg" - } - ], - "components": [ - { - "id": 2941, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2942, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2943, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2944, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - }, - { - "id": 2945, - "name": "Toaster Component 5", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 294, - "url": "https://via.placeholder.com/400x300/047deb/ffffff?text=NeoCook+Toaster+589A", - "description": "Toaster product image" - } - ], - "created_at": "2024-10-07T09:16:11.467037Z", - "updated_at": "2025-07-23T09:16:11.467037Z" - }, - { - "id": 295, - "name": "ZenGear Kettle 454K", - "description": "ZenGear Kettle 454K is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.01, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2171, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 41.6, - "mass": 1.1, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 28.0, - "mass": 0.4, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 30.4, - "mass": 0.73, - "unit": "kg" - } - ], - "components": [ - { - "id": 2951, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2952, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2953, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2954, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 295, - "url": "https://via.placeholder.com/400x300/070ae7/ffffff?text=ZenGear+Kettle+454K", - "description": "Kettle product image" - } - ], - "created_at": "2024-12-22T09:16:11.467091Z", - "updated_at": "2025-05-29T09:16:11.467091Z" - }, - { - "id": 296, - "name": "SmartHome Rice Cooker 275T", - "description": "SmartHome Rice Cooker 275T is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.3, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1067, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 50.7, - "mass": 0.78, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 49.3, - "mass": 1.11, - "unit": "kg" - } - ], - "components": [ - { - "id": 2961, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2962, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2963, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 296, - "url": "https://via.placeholder.com/400x300/09afef/ffffff?text=SmartHome+Rice+Cooker+275T", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-11-07T09:16:11.467155Z", - "updated_at": "2025-05-23T09:16:11.467155Z" - }, - { - "id": 297, - "name": "ChefMate Monitor 548Y", - "description": "ChefMate Monitor 548Y is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 4.0, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2052, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 25.0, - "mass": 0.33, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 75.0, - "mass": 2.75, - "unit": "kg" - } - ], - "components": [ - { - "id": 2971, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2972, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2973, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2974, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 2975, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 297, - "url": "https://via.placeholder.com/400x300/02c755/ffffff?text=ChefMate+Monitor+548Y", - "description": "Monitor product image" - } - ], - "created_at": "2024-04-09T09:16:11.467200Z", - "updated_at": "2025-07-02T09:16:11.467200Z" - }, - { - "id": 298, - "name": "CleanWave Kettle 973Q", - "description": "CleanWave Kettle 973Q is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.73, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 915, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 71.4, - "mass": 0.94, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 28.6, - "mass": 1.03, - "unit": "kg" - } - ], - "components": [ - { - "id": 2981, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2982, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2983, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 2984, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 298, - "url": "https://via.placeholder.com/400x300/0d57f8/ffffff?text=CleanWave+Kettle+973Q", - "description": "Kettle product image" - } - ], - "created_at": "2024-12-02T09:16:11.467253Z", - "updated_at": "2025-05-08T09:16:11.467253Z" - }, - { - "id": 299, - "name": "NeoCook Rice Cooker 872D", - "description": "NeoCook Rice Cooker 872D is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.07, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1987, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 57.7, - "mass": 1.71, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 42.3, - "mass": 1.41, - "unit": "kg" - } - ], - "components": [ - { - "id": 2991, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2992, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2993, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 2994, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 299, - "url": "https://via.placeholder.com/400x300/0b9d23/ffffff?text=NeoCook+Rice+Cooker+872D", - "description": "Rice Cooker product image" - } - ], - "created_at": "2025-02-17T09:16:11.467285Z", - "updated_at": "2025-07-15T09:16:11.467285Z" - }, - { - "id": 300, - "name": "EcoTech Toaster 625V", - "description": "EcoTech Toaster 625V is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.88, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 940, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 83.9, - "mass": 2.22, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 16.1, - "mass": 0.3, - "unit": "kg" - } - ], - "components": [ - { - "id": 3001, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3002, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 300, - "url": "https://via.placeholder.com/400x300/0b4f56/ffffff?text=EcoTech+Toaster+625V", - "description": "Toaster product image" - } - ], - "created_at": "2024-09-30T09:16:11.467366Z", - "updated_at": "2025-07-14T09:16:11.467366Z" - }, - { - "id": 301, - "name": "NeoCook Toaster 322Y", - "description": "NeoCook Toaster 322Y is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.67, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1158, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 23.0, - "mass": 0.32, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 26.3, - "mass": 0.85, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 21.7, - "mass": 0.34, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 28.9, - "mass": 0.35, - "unit": "kg" - } - ], - "components": [ - { - "id": 3011, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3012, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3013, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3014, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3015, - "name": "Toaster Component 5", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 301, - "url": "https://via.placeholder.com/400x300/0431a4/ffffff?text=NeoCook+Toaster+322Y", - "description": "Toaster product image" - } - ], - "created_at": "2024-12-13T09:16:11.467421Z", - "updated_at": "2025-06-18T09:16:11.467421Z" - }, - { - "id": 302, - "name": "ChefMate Coffee Maker 427Z", - "description": "ChefMate Coffee Maker 427Z is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.89, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1856, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 44.1, - "mass": 1.46, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 41.9, - "mass": 1.2, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 14.0, - "mass": 0.43, - "unit": "kg" - } - ], - "components": [ - { - "id": 3021, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3022, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 302, - "url": "https://via.placeholder.com/400x300/01c354/ffffff?text=ChefMate+Coffee+Maker+427Z", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-10-29T09:16:11.467455Z", - "updated_at": "2025-06-09T09:16:11.467455Z" - }, - { - "id": 303, - "name": "CleanWave Rice Cooker 554H", - "description": "CleanWave Rice Cooker 554H is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 4.0, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1741, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 66.7, - "mass": 0.79, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 33.3, - "mass": 0.99, - "unit": "kg" - } - ], - "components": [ - { - "id": 3031, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3032, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3033, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3034, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 303, - "url": "https://via.placeholder.com/400x300/0258f8/ffffff?text=CleanWave+Rice+Cooker+554H", - "description": "Rice Cooker product image" - } - ], - "created_at": "2025-04-17T09:16:11.467490Z", - "updated_at": "2025-06-12T09:16:11.467490Z" - }, - { - "id": 304, - "name": "PureLife Monitor 896D", - "description": "PureLife Monitor 896D is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.66, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1539, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 65.6, - "mass": 2.59, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 34.4, - "mass": 0.86, - "unit": "kg" - } - ], - "components": [ - { - "id": 3041, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3042, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3043, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3044, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3045, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 304, - "url": "https://via.placeholder.com/400x300/0d9540/ffffff?text=PureLife+Monitor+896D", - "description": "Monitor product image" - } - ], - "created_at": "2024-08-17T09:16:11.467526Z", - "updated_at": "2025-06-22T09:16:11.467526Z" - }, - { - "id": 305, - "name": "ChefMate Humidifier 361F", - "description": "ChefMate Humidifier 361F is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.48, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1646, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 15.8, - "mass": 0.41, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 52.6, - "mass": 0.87, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 31.6, - "mass": 1.04, - "unit": "kg" - } - ], - "components": [ - { - "id": 3051, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3052, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3053, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3054, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 305, - "url": "https://via.placeholder.com/400x300/0df883/ffffff?text=ChefMate+Humidifier+361F", - "description": "Humidifier product image" - } - ], - "created_at": "2024-09-09T09:16:11.467566Z", - "updated_at": "2025-05-13T09:16:11.467566Z" - }, - { - "id": 306, - "name": "ZenGear Air Purifier 689E", - "description": "ZenGear Air Purifier 689E is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.31, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1283, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 12.6, - "mass": 0.4, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 52.9, - "mass": 0.91, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 34.5, - "mass": 1.09, - "unit": "kg" - } - ], - "components": [ - { - "id": 3061, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 3062, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 306, - "url": "https://via.placeholder.com/400x300/0ace8f/ffffff?text=ZenGear+Air+Purifier+689E", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-10-12T09:16:11.467594Z", - "updated_at": "2025-06-04T09:16:11.467594Z" - }, - { - "id": 307, - "name": "ZenGear Kettle 641K", - "description": "ZenGear Kettle 641K is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.94, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1182, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 40.4, - "mass": 0.4, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 59.6, - "mass": 1.25, - "unit": "kg" - } - ], - "components": [ - { - "id": 3071, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3072, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3073, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 307, - "url": "https://via.placeholder.com/400x300/0a6acf/ffffff?text=ZenGear+Kettle+641K", - "description": "Kettle product image" - } - ], - "created_at": "2025-01-07T09:16:11.467646Z", - "updated_at": "2025-05-29T09:16:11.467646Z" - }, - { - "id": 308, - "name": "SmartHome Fan 241E", - "description": "SmartHome Fan 241E is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.27, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2101, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 82.1, - "mass": 1.56, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 17.9, - "mass": 0.35, - "unit": "kg" - } - ], - "components": [ - { - "id": 3081, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 3082, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 3083, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 3084, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 308, - "url": "https://via.placeholder.com/400x300/044a03/ffffff?text=SmartHome+Fan+241E", - "description": "Fan product image" - } - ], - "created_at": "2025-04-22T09:16:11.467680Z", - "updated_at": "2025-05-15T09:16:11.467680Z" - }, - { - "id": 309, - "name": "EcoTech Humidifier 836F", - "description": "EcoTech Humidifier 836F is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.62, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1662, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 15.4, - "mass": 0.41, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 57.1, - "mass": 1.72, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 27.5, - "mass": 0.62, - "unit": "kg" - } - ], - "components": [ - { - "id": 3091, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3092, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3093, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 309, - "url": "https://via.placeholder.com/400x300/035fdb/ffffff?text=EcoTech+Humidifier+836F", - "description": "Humidifier product image" - } - ], - "created_at": "2024-11-18T09:16:11.467739Z", - "updated_at": "2025-06-03T09:16:11.467739Z" - }, - { - "id": 310, - "name": "NeoCook Toaster 361H", - "description": "NeoCook Toaster 361H is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.34, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1621, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 34.9, - "mass": 1.31, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 11.6, - "mass": 0.26, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 23.3, - "mass": 0.3, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 30.2, - "mass": 0.77, - "unit": "kg" - } - ], - "components": [ - { - "id": 3101, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3102, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3103, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3104, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 310, - "url": "https://via.placeholder.com/400x300/097dcc/ffffff?text=NeoCook+Toaster+361H", - "description": "Toaster product image" - } - ], - "created_at": "2024-09-18T09:16:11.467794Z", - "updated_at": "2025-07-13T09:16:11.467794Z" - }, - { - "id": 311, - "name": "EcoTech Rice Cooker 509M", - "description": "EcoTech Rice Cooker 509M is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.11, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2053, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 39.5, - "mass": 1.49, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 60.5, - "mass": 2.08, - "unit": "kg" - } - ], - "components": [ - { - "id": 3111, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3112, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 311, - "url": "https://via.placeholder.com/400x300/0dddfe/ffffff?text=EcoTech+Rice+Cooker+509M", - "description": "Rice Cooker product image" - } - ], - "created_at": "2025-03-01T09:16:11.467839Z", - "updated_at": "2025-07-27T09:16:11.467839Z" - }, - { - "id": 312, - "name": "ZenGear Rice Cooker 661J", - "description": "ZenGear Rice Cooker 661J is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.28, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 802, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 21.1, - "mass": 0.62, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 25.2, - "mass": 0.45, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 25.2, - "mass": 0.84, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 28.5, - "mass": 0.57, - "unit": "kg" - } - ], - "components": [ - { - "id": 3121, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3122, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 312, - "url": "https://via.placeholder.com/400x300/0acc10/ffffff?text=ZenGear+Rice+Cooker+661J", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-11-11T09:16:11.467887Z", - "updated_at": "2025-07-20T09:16:11.467887Z" - }, - { - "id": 313, - "name": "CleanWave Fan 262Y", - "description": "CleanWave Fan 262Y is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.25, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 802, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 28.3, - "mass": 0.37, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 32.6, - "mass": 0.71, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 39.1, - "mass": 1.25, - "unit": "kg" - } - ], - "components": [ - { - "id": 3131, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 3132, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 3133, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 3134, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 313, - "url": "https://via.placeholder.com/400x300/08e7cd/ffffff?text=CleanWave+Fan+262Y", - "description": "Fan product image" - } - ], - "created_at": "2024-06-04T09:16:11.467924Z", - "updated_at": "2025-05-01T09:16:11.467924Z" - }, - { - "id": 314, - "name": "ZenGear Monitor 319J", - "description": "ZenGear Monitor 319J is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.14, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1444, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 50.0, - "mass": 1.06, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 50.0, - "mass": 1.83, - "unit": "kg" - } - ], - "components": [ - { - "id": 3141, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3142, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3143, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3144, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3145, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 314, - "url": "https://via.placeholder.com/400x300/053af8/ffffff?text=ZenGear+Monitor+319J", - "description": "Monitor product image" - } - ], - "created_at": "2025-01-25T09:16:11.467976Z", - "updated_at": "2025-06-08T09:16:11.467976Z" - }, - { - "id": 315, - "name": "PureLife Toaster 140K", - "description": "PureLife Toaster 140K is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.57, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 816, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 30.3, - "mass": 1.2, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 51.5, - "mass": 1.44, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 18.2, - "mass": 0.46, - "unit": "kg" - } - ], - "components": [ - { - "id": 3151, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3152, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3153, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3154, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 315, - "url": "https://via.placeholder.com/400x300/0dc4e0/ffffff?text=PureLife+Toaster+140K", - "description": "Toaster product image" - } - ], - "created_at": "2024-10-24T09:16:11.468019Z", - "updated_at": "2025-06-30T09:16:11.468019Z" - }, - { - "id": 316, - "name": "CleanWave Fan 867B", - "description": "CleanWave Fan 867B is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.73, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2099, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 51.0, - "mass": 1.63, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 49.0, - "mass": 1.23, - "unit": "kg" - } - ], - "components": [ - { - "id": 3161, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 3162, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 3163, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 3164, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 316, - "url": "https://via.placeholder.com/400x300/0b33d5/ffffff?text=CleanWave+Fan+867B", - "description": "Fan product image" - } - ], - "created_at": "2024-08-31T09:16:11.468074Z", - "updated_at": "2025-05-09T09:16:11.468074Z" - }, - { - "id": 317, - "name": "AquaPro Rice Cooker 996T", - "description": "AquaPro Rice Cooker 996T is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.36, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1717, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 20.5, - "mass": 0.22, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 21.9, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 57.5, - "mass": 0.89, - "unit": "kg" - } - ], - "components": [ - { - "id": 3171, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3172, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3173, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3174, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3175, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 317, - "url": "https://via.placeholder.com/400x300/0b37e0/ffffff?text=AquaPro+Rice+Cooker+996T", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-12-25T09:16:11.468115Z", - "updated_at": "2025-06-01T09:16:11.468115Z" - }, - { - "id": 318, - "name": "ZenGear Iron 445Y", - "description": "ZenGear Iron 445Y is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.57, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 925, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 16.9, - "mass": 0.28, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 25.4, - "mass": 0.57, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 17.8, - "mass": 0.59, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 39.8, - "mass": 0.91, - "unit": "kg" - } - ], - "components": [ - { - "id": 3181, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 3182, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 3183, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 318, - "url": "https://via.placeholder.com/400x300/042de4/ffffff?text=ZenGear+Iron+445Y", - "description": "Iron product image" - } - ], - "created_at": "2025-01-15T09:16:11.468166Z", - "updated_at": "2025-06-28T09:16:11.468166Z" - }, - { - "id": 319, - "name": "EcoTech Humidifier 976X", - "description": "EcoTech Humidifier 976X is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.15, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1850, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 17.6, - "mass": 0.7, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 45.6, - "mass": 1.31, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 36.8, - "mass": 0.87, - "unit": "kg" - } - ], - "components": [ - { - "id": 3191, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3192, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3193, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3194, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 319, - "url": "https://via.placeholder.com/400x300/078ec7/ffffff?text=EcoTech+Humidifier+976X", - "description": "Humidifier product image" - } - ], - "created_at": "2024-11-28T09:16:11.468216Z", - "updated_at": "2025-07-17T09:16:11.468216Z" - }, - { - "id": 320, - "name": "NeoCook Rice Cooker 354J", - "description": "NeoCook Rice Cooker 354J is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.97, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1112, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 24.0, - "mass": 0.36, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 27.0, - "mass": 0.37, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 27.0, - "mass": 0.86, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 22.0, - "mass": 0.79, - "unit": "kg" - } - ], - "components": [ - { - "id": 3201, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3202, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3203, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 320, - "url": "https://via.placeholder.com/400x300/0a8999/ffffff?text=NeoCook+Rice+Cooker+354J", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-04-16T09:16:11.468269Z", - "updated_at": "2025-07-30T09:16:11.468269Z" - }, - { - "id": 321, - "name": "ZenGear Blender 330X", - "description": "ZenGear Blender 330X is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.27, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1383, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 36.8, - "mass": 1.08, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 34.7, - "mass": 0.92, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 28.5, - "mass": 0.36, - "unit": "kg" - } - ], - "components": [ - { - "id": 3211, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 3212, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 321, - "url": "https://via.placeholder.com/400x300/0269b9/ffffff?text=ZenGear+Blender+330X", - "description": "Blender product image" - } - ], - "created_at": "2025-04-20T09:16:11.468306Z", - "updated_at": "2025-05-31T09:16:11.468306Z" - }, - { - "id": 322, - "name": "ZenGear Toaster 810D", - "description": "ZenGear Toaster 810D is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.33, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 809, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 19.9, - "mass": 0.33, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 28.3, - "mass": 0.98, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 26.7, - "mass": 0.8, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 25.1, - "mass": 0.43, - "unit": "kg" - } - ], - "components": [ - { - "id": 3221, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3222, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3223, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3224, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3225, - "name": "Toaster Component 5", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 322, - "url": "https://via.placeholder.com/400x300/0588a4/ffffff?text=ZenGear+Toaster+810D", - "description": "Toaster product image" - } - ], - "created_at": "2024-11-23T09:16:11.468350Z", - "updated_at": "2025-06-10T09:16:11.468350Z" - }, - { - "id": 323, - "name": "ChefMate Toaster 771L", - "description": "ChefMate Toaster 771L is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.31, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1775, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 21.8, - "mass": 0.66, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 29.1, - "mass": 1.12, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 28.5, - "mass": 0.97, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 20.6, - "mass": 0.52, - "unit": "kg" - } - ], - "components": [ - { - "id": 3231, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3232, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 323, - "url": "https://via.placeholder.com/400x300/0a5a35/ffffff?text=ChefMate+Toaster+771L", - "description": "Toaster product image" - } - ], - "created_at": "2024-04-26T09:16:11.468385Z", - "updated_at": "2025-04-26T09:16:11.468385Z" - }, - { - "id": 324, - "name": "EcoTech Iron 631B", - "description": "EcoTech Iron 631B is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.48, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1057, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 65.1, - "mass": 2.38, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 34.9, - "mass": 0.46, - "unit": "kg" - } - ], - "components": [ - { - "id": 3241, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 3242, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 324, - "url": "https://via.placeholder.com/400x300/024df6/ffffff?text=EcoTech+Iron+631B", - "description": "Iron product image" - } - ], - "created_at": "2024-07-03T09:16:11.468418Z", - "updated_at": "2025-06-16T09:16:11.468418Z" - }, - { - "id": 325, - "name": "CleanWave Iron 517C", - "description": "CleanWave Iron 517C is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.51, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1697, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 64.3, - "mass": 0.86, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 35.7, - "mass": 1.13, - "unit": "kg" - } - ], - "components": [ - { - "id": 3251, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 3252, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 3253, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 325, - "url": "https://via.placeholder.com/400x300/06e906/ffffff?text=CleanWave+Iron+517C", - "description": "Iron product image" - } - ], - "created_at": "2025-01-12T09:16:11.468465Z", - "updated_at": "2025-06-30T09:16:11.468465Z" - }, - { - "id": 326, - "name": "ChefMate Kettle 300J", - "description": "ChefMate Kettle 300J is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.36, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 30.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 822, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 76.9, - "mass": 2.51, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 23.1, - "mass": 0.88, - "unit": "kg" - } - ], - "components": [ - { - "id": 3261, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3262, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3263, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3264, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3265, - "name": "Kettle Component 5", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 326, - "url": "https://via.placeholder.com/400x300/029df8/ffffff?text=ChefMate+Kettle+300J", - "description": "Kettle product image" - } - ], - "created_at": "2024-11-26T09:16:11.468516Z", - "updated_at": "2025-05-03T09:16:11.468516Z" - }, - { - "id": 327, - "name": "PureLife Rice Cooker 128T", - "description": "PureLife Rice Cooker 128T is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.03, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1137, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 35.4, - "mass": 1.24, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 64.6, - "mass": 2.37, - "unit": "kg" - } - ], - "components": [ - { - "id": 3271, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3272, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3273, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3274, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 327, - "url": "https://via.placeholder.com/400x300/0410ea/ffffff?text=PureLife+Rice+Cooker+128T", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-07-29T09:16:11.468557Z", - "updated_at": "2025-05-23T09:16:11.468557Z" - }, - { - "id": 328, - "name": "AquaPro Humidifier 967Z", - "description": "AquaPro Humidifier 967Z is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.87, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2188, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 38.6, - "mass": 1.3, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 45.5, - "mass": 1.74, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 15.9, - "mass": 0.61, - "unit": "kg" - } - ], - "components": [ - { - "id": 3281, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3282, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 328, - "url": "https://via.placeholder.com/400x300/098807/ffffff?text=AquaPro+Humidifier+967Z", - "description": "Humidifier product image" - } - ], - "created_at": "2025-02-27T09:16:11.469090Z", - "updated_at": "2025-07-07T09:16:11.469090Z" - }, - { - "id": 329, - "name": "EcoTech Monitor 924D", - "description": "EcoTech Monitor 924D is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.96, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1508, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 51.3, - "mass": 0.92, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 15.4, - "mass": 0.44, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 33.3, - "mass": 0.57, - "unit": "kg" - } - ], - "components": [ - { - "id": 3291, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3292, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3293, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 329, - "url": "https://via.placeholder.com/400x300/0721a4/ffffff?text=EcoTech+Monitor+924D", - "description": "Monitor product image" - } - ], - "created_at": "2025-03-20T09:16:11.469187Z", - "updated_at": "2025-06-09T09:16:11.469187Z" - }, - { - "id": 330, - "name": "NeoCook Humidifier 208N", - "description": "NeoCook Humidifier 208N is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.16, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 845, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 28.3, - "mass": 0.67, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 33.3, - "mass": 0.56, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 38.3, - "mass": 1.45, - "unit": "kg" - } - ], - "components": [ - { - "id": 3301, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3302, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 330, - "url": "https://via.placeholder.com/400x300/089931/ffffff?text=NeoCook+Humidifier+208N", - "description": "Humidifier product image" - } - ], - "created_at": "2025-01-03T09:16:11.469239Z", - "updated_at": "2025-05-30T09:16:11.469239Z" - }, - { - "id": 331, - "name": "PureLife Humidifier 211K", - "description": "PureLife Humidifier 211K is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.03, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1525, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 19.6, - "mass": 0.62, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 62.5, - "mass": 0.91, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 17.9, - "mass": 0.24, - "unit": "kg" - } - ], - "components": [ - { - "id": 3311, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3312, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3313, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 331, - "url": "https://via.placeholder.com/400x300/07c140/ffffff?text=PureLife+Humidifier+211K", - "description": "Humidifier product image" - } - ], - "created_at": "2025-01-08T09:16:11.469310Z", - "updated_at": "2025-07-10T09:16:11.469310Z" - }, - { - "id": 332, - "name": "CleanWave Iron 692Y", - "description": "CleanWave Iron 692Y is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.2, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1968, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 47.5, - "mass": 0.86, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 31.3, - "mass": 0.77, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 21.2, - "mass": 0.58, - "unit": "kg" - } - ], - "components": [ - { - "id": 3321, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 3322, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 3323, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 3324, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - }, - { - "id": 3325, - "name": "Iron Component 5", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 332, - "url": "https://via.placeholder.com/400x300/02bae0/ffffff?text=CleanWave+Iron+692Y", - "description": "Iron product image" - } - ], - "created_at": "2024-11-27T09:16:11.469375Z", - "updated_at": "2025-06-11T09:16:11.469375Z" - }, - { - "id": 333, - "name": "AquaPro Rice Cooker 980I", - "description": "AquaPro Rice Cooker 980I is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.7, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1593, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 30.7, - "mass": 0.45, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 27.1, - "mass": 0.56, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 24.5, - "mass": 0.9, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 17.7, - "mass": 0.62, - "unit": "kg" - } - ], - "components": [ - { - "id": 3331, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3332, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 333, - "url": "https://via.placeholder.com/400x300/05a542/ffffff?text=AquaPro+Rice+Cooker+980I", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-11-06T09:16:11.469437Z", - "updated_at": "2025-06-18T09:16:11.469437Z" - }, - { - "id": 334, - "name": "SmartHome Toaster 935W", - "description": "SmartHome Toaster 935W is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.16, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1198, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 72.1, - "mass": 0.88, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 27.9, - "mass": 0.51, - "unit": "kg" - } - ], - "components": [ - { - "id": 3341, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3342, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3343, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3344, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3345, - "name": "Toaster Component 5", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 334, - "url": "https://via.placeholder.com/400x300/03c9b8/ffffff?text=SmartHome+Toaster+935W", - "description": "Toaster product image" - } - ], - "created_at": "2025-03-31T09:16:11.469489Z", - "updated_at": "2025-07-21T09:16:11.469489Z" - }, - { - "id": 335, - "name": "NeoCook Kettle 572F", - "description": "NeoCook Kettle 572F is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.88, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1811, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 53.6, - "mass": 0.72, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 46.4, - "mass": 1.7, - "unit": "kg" - } - ], - "components": [ - { - "id": 3351, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3352, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3353, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 335, - "url": "https://via.placeholder.com/400x300/0afd50/ffffff?text=NeoCook+Kettle+572F", - "description": "Kettle product image" - } - ], - "created_at": "2024-10-28T09:16:11.469630Z", - "updated_at": "2025-07-11T09:16:11.469630Z" - }, - { - "id": 336, - "name": "ZenGear Humidifier 669I", - "description": "ZenGear Humidifier 669I is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.34, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1315, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 52.6, - "mass": 0.81, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 47.4, - "mass": 0.97, - "unit": "kg" - } - ], - "components": [ - { - "id": 3361, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3362, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3363, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 336, - "url": "https://via.placeholder.com/400x300/056c2d/ffffff?text=ZenGear+Humidifier+669I", - "description": "Humidifier product image" - } - ], - "created_at": "2024-03-30T09:16:11.469682Z", - "updated_at": "2025-06-13T09:16:11.469682Z" - }, - { - "id": 337, - "name": "SmartHome Toaster 335A", - "description": "SmartHome Toaster 335A is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.61, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 995, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 60.0, - "mass": 1.05, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 12.9, - "mass": 0.19, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 27.1, - "mass": 0.78, - "unit": "kg" - } - ], - "components": [ - { - "id": 3371, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3372, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3373, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3374, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 337, - "url": "https://via.placeholder.com/400x300/0a8fc6/ffffff?text=SmartHome+Toaster+335A", - "description": "Toaster product image" - } - ], - "created_at": "2024-09-03T09:16:11.469753Z", - "updated_at": "2025-07-02T09:16:11.469753Z" - }, - { - "id": 338, - "name": "NeoCook Iron 651R", - "description": "NeoCook Iron 651R is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.93, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1183, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 50.6, - "mass": 0.75, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 19.8, - "mass": 0.21, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 29.6, - "mass": 0.79, - "unit": "kg" - } - ], - "components": [ - { - "id": 3381, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 3382, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 3383, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 338, - "url": "https://via.placeholder.com/400x300/050ddd/ffffff?text=NeoCook+Iron+651R", - "description": "Iron product image" - } - ], - "created_at": "2025-04-12T09:16:11.469825Z", - "updated_at": "2025-05-11T09:16:11.469825Z" - }, - { - "id": 339, - "name": "ZenGear Humidifier 469E", - "description": "ZenGear Humidifier 469E is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.97, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1106, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 40.9, - "mass": 0.79, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 59.1, - "mass": 2.05, - "unit": "kg" - } - ], - "components": [ - { - "id": 3391, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3392, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3393, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 339, - "url": "https://via.placeholder.com/400x300/079a20/ffffff?text=ZenGear+Humidifier+469E", - "description": "Humidifier product image" - } - ], - "created_at": "2024-04-27T09:16:11.469889Z", - "updated_at": "2025-06-16T09:16:11.469889Z" - }, - { - "id": 340, - "name": "ChefMate Blender 406Y", - "description": "ChefMate Blender 406Y is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.22, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2101, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 21.3, - "mass": 0.84, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 78.7, - "mass": 2.06, - "unit": "kg" - } - ], - "components": [ - { - "id": 3401, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 3402, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 340, - "url": "https://via.placeholder.com/400x300/08513b/ffffff?text=ChefMate+Blender+406Y", - "description": "Blender product image" - } - ], - "created_at": "2024-10-28T09:16:11.469934Z", - "updated_at": "2025-06-25T09:16:11.469934Z" - }, - { - "id": 341, - "name": "SmartHome Iron 936J", - "description": "SmartHome Iron 936J is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.04, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 895, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 10.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 37.1, - "mass": 1.16, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 14.3, - "mass": 0.33, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 37.1, - "mass": 0.99, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 11.4, - "mass": 0.31, - "unit": "kg" - } - ], - "components": [ - { - "id": 3411, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 3412, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 3413, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 3414, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 341, - "url": "https://via.placeholder.com/400x300/07dffb/ffffff?text=SmartHome+Iron+936J", - "description": "Iron product image" - } - ], - "created_at": "2024-05-08T09:16:11.469997Z", - "updated_at": "2025-07-20T09:16:11.469997Z" - }, - { - "id": 342, - "name": "ChefMate Coffee Maker 531I", - "description": "ChefMate Coffee Maker 531I is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.39, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1245, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 49.1, - "mass": 1.49, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 50.9, - "mass": 1.48, - "unit": "kg" - } - ], - "components": [ - { - "id": 3421, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3422, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3423, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 342, - "url": "https://via.placeholder.com/400x300/04f9ff/ffffff?text=ChefMate+Coffee+Maker+531I", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-07-21T09:16:11.470050Z", - "updated_at": "2025-05-17T09:16:11.470050Z" - }, - { - "id": 343, - "name": "AquaPro Rice Cooker 663X", - "description": "AquaPro Rice Cooker 663X is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.33, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 941, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 28.1, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 8.6, - "mass": 0.29, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 20.1, - "mass": 0.5, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 43.2, - "mass": 1.44, - "unit": "kg" - } - ], - "components": [ - { - "id": 3431, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3432, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3433, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3434, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3435, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 343, - "url": "https://via.placeholder.com/400x300/0d122c/ffffff?text=AquaPro+Rice+Cooker+663X", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-10-30T09:16:11.470170Z", - "updated_at": "2025-07-29T09:16:11.470170Z" - }, - { - "id": 344, - "name": "SmartHome Iron 640P", - "description": "SmartHome Iron 640P is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.27, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1638, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 30.1, - "mass": 0.83, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 38.4, - "mass": 0.89, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 31.5, - "mass": 0.65, - "unit": "kg" - } - ], - "components": [ - { - "id": 3441, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 3442, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 344, - "url": "https://via.placeholder.com/400x300/0a81f0/ffffff?text=SmartHome+Iron+640P", - "description": "Iron product image" - } - ], - "created_at": "2025-01-02T09:16:11.470225Z", - "updated_at": "2025-07-22T09:16:11.470225Z" - }, - { - "id": 345, - "name": "EcoTech Fan 472L", - "description": "EcoTech Fan 472L is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.23, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1905, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 29.3, - "mass": 0.9, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 47.2, - "mass": 1.88, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 23.6, - "mass": 0.48, - "unit": "kg" - } - ], - "components": [ - { - "id": 3451, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 3452, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 3453, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 3454, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - }, - { - "id": 3455, - "name": "Fan Component 5", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 345, - "url": "https://via.placeholder.com/400x300/05d410/ffffff?text=EcoTech+Fan+472L", - "description": "Fan product image" - } - ], - "created_at": "2025-03-19T09:16:11.470289Z", - "updated_at": "2025-07-27T09:16:11.470289Z" - }, - { - "id": 346, - "name": "CleanWave Coffee Maker 440K", - "description": "CleanWave Coffee Maker 440K is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.77, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2028, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 34.0, - "mass": 1.19, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 9.6, - "mass": 0.12, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 19.9, - "mass": 0.79, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 36.5, - "mass": 1.3, - "unit": "kg" - } - ], - "components": [ - { - "id": 3461, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3462, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3463, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3464, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 346, - "url": "https://via.placeholder.com/400x300/08f70c/ffffff?text=CleanWave+Coffee+Maker+440K", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-08-03T09:16:11.470354Z", - "updated_at": "2025-07-03T09:16:11.470354Z" - }, - { - "id": 347, - "name": "SmartHome Blender 611E", - "description": "SmartHome Blender 611E is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.73, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1140, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 50.0, - "mass": 1.26, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 50.0, - "mass": 0.99, - "unit": "kg" - } - ], - "components": [ - { - "id": 3471, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 3472, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 3473, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 3474, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 347, - "url": "https://via.placeholder.com/400x300/02da67/ffffff?text=SmartHome+Blender+611E", - "description": "Blender product image" - } - ], - "created_at": "2025-02-28T09:16:11.470422Z", - "updated_at": "2025-04-26T09:16:11.470422Z" - }, - { - "id": 348, - "name": "SmartHome Monitor 422N", - "description": "SmartHome Monitor 422N is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.66, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1839, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 34.7, - "mass": 0.86, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 14.7, - "mass": 0.39, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 33.3, - "mass": 1.01, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 17.3, - "mass": 0.38, - "unit": "kg" - } - ], - "components": [ - { - "id": 3481, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3482, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3483, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3484, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3485, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 348, - "url": "https://via.placeholder.com/400x300/0ef575/ffffff?text=SmartHome+Monitor+422N", - "description": "Monitor product image" - } - ], - "created_at": "2025-04-14T09:16:11.470632Z", - "updated_at": "2025-05-28T09:16:11.470632Z" - }, - { - "id": 349, - "name": "AquaPro Coffee Maker 808N", - "description": "AquaPro Coffee Maker 808N is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.43, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1915, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 37.3, - "mass": 1.32, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 62.7, - "mass": 1.98, - "unit": "kg" - } - ], - "components": [ - { - "id": 3491, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3492, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3493, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3494, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 349, - "url": "https://via.placeholder.com/400x300/0e9510/ffffff?text=AquaPro+Coffee+Maker+808N", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-07-22T09:16:11.470689Z", - "updated_at": "2025-06-18T09:16:11.470689Z" - }, - { - "id": 350, - "name": "PureLife Monitor 123W", - "description": "PureLife Monitor 123W is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.78, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1123, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 23.2, - "mass": 0.38, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 34.8, - "mass": 1.28, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 42.0, - "mass": 1.1, - "unit": "kg" - } - ], - "components": [ - { - "id": 3501, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3502, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 350, - "url": "https://via.placeholder.com/400x300/0cb13a/ffffff?text=PureLife+Monitor+123W", - "description": "Monitor product image" - } - ], - "created_at": "2025-03-22T09:16:11.470746Z", - "updated_at": "2025-06-22T09:16:11.470746Z" - }, - { - "id": 351, - "name": "SmartHome Iron 238Z", - "description": "SmartHome Iron 238Z is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.3, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1109, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 32.2, - "mass": 0.57, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 48.3, - "mass": 1.78, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 8.5, - "mass": 0.33, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 11.0, - "mass": 0.29, - "unit": "kg" - } - ], - "components": [ - { - "id": 3511, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 3512, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 3513, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 3514, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 351, - "url": "https://via.placeholder.com/400x300/02560e/ffffff?text=SmartHome+Iron+238Z", - "description": "Iron product image" - } - ], - "created_at": "2024-11-15T09:16:11.470806Z", - "updated_at": "2025-06-15T09:16:11.470806Z" - }, - { - "id": 352, - "name": "ChefMate Fan 327O", - "description": "ChefMate Fan 327O is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.69, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1372, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 17.0, - "mass": 0.61, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 38.5, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 44.4, - "mass": 0.59, - "unit": "kg" - } - ], - "components": [ - { - "id": 3521, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 3522, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 3523, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 3524, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - }, - { - "id": 3525, - "name": "Fan Component 5", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 352, - "url": "https://via.placeholder.com/400x300/05c0db/ffffff?text=ChefMate+Fan+327O", - "description": "Fan product image" - } - ], - "created_at": "2024-07-22T09:16:11.470863Z", - "updated_at": "2025-04-29T09:16:11.470863Z" - }, - { - "id": 353, - "name": "ChefMate Iron 598X", - "description": "ChefMate Iron 598X is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.19, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1061, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 25.3, - "mass": 0.83, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 25.3, - "mass": 0.84, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 22.3, - "mass": 0.73, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 27.1, - "mass": 1.01, - "unit": "kg" - } - ], - "components": [ - { - "id": 3531, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 3532, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 3533, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 3534, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - }, - { - "id": 3535, - "name": "Iron Component 5", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 353, - "url": "https://via.placeholder.com/400x300/0c2aa5/ffffff?text=ChefMate+Iron+598X", - "description": "Iron product image" - } - ], - "created_at": "2025-03-23T09:16:11.470937Z", - "updated_at": "2025-05-05T09:16:11.470937Z" - }, - { - "id": 354, - "name": "EcoTech Blender 942N", - "description": "EcoTech Blender 942N is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.47, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1573, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 19.3, - "mass": 0.51, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 51.1, - "mass": 2.0, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 29.5, - "mass": 1.15, - "unit": "kg" - } - ], - "components": [ - { - "id": 3541, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 3542, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 354, - "url": "https://via.placeholder.com/400x300/08e40f/ffffff?text=EcoTech+Blender+942N", - "description": "Blender product image" - } - ], - "created_at": "2024-08-25T09:16:11.470986Z", - "updated_at": "2025-06-16T09:16:11.470986Z" - }, - { - "id": 355, - "name": "SmartHome Fan 524Y", - "description": "SmartHome Fan 524Y is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.76, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1854, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 35.4, - "mass": 0.64, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 34.8, - "mass": 1.11, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 29.7, - "mass": 0.93, - "unit": "kg" - } - ], - "components": [ - { - "id": 3551, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 3552, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 3553, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 355, - "url": "https://via.placeholder.com/400x300/02a511/ffffff?text=SmartHome+Fan+524Y", - "description": "Fan product image" - } - ], - "created_at": "2025-02-24T09:16:11.471032Z", - "updated_at": "2025-06-02T09:16:11.471032Z" - }, - { - "id": 356, - "name": "CleanWave Humidifier 360F", - "description": "CleanWave Humidifier 360F is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.08, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1294, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 30.0, - "mass": 0.85, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 41.4, - "mass": 0.54, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 28.6, - "mass": 0.75, - "unit": "kg" - } - ], - "components": [ - { - "id": 3561, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3562, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3563, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3564, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 356, - "url": "https://via.placeholder.com/400x300/03beb5/ffffff?text=CleanWave+Humidifier+360F", - "description": "Humidifier product image" - } - ], - "created_at": "2024-06-26T09:16:11.471106Z", - "updated_at": "2025-04-24T09:16:11.471106Z" - }, - { - "id": 357, - "name": "AquaPro Air Purifier 208Z", - "description": "AquaPro Air Purifier 208Z is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.52, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1745, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 24.8, - "mass": 0.27, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 38.3, - "mass": 0.84, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 36.9, - "mass": 0.9, - "unit": "kg" - } - ], - "components": [ - { - "id": 3571, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 3572, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 3573, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 3574, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 3575, - "name": "Air Purifier Component 5", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 357, - "url": "https://via.placeholder.com/400x300/06862a/ffffff?text=AquaPro+Air+Purifier+208Z", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-10-18T09:16:11.471158Z", - "updated_at": "2025-05-26T09:16:11.471158Z" - }, - { - "id": 358, - "name": "ZenGear Monitor 388P", - "description": "ZenGear Monitor 388P is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.92, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1352, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 43.2, - "mass": 0.49, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 56.8, - "mass": 1.15, - "unit": "kg" - } - ], - "components": [ - { - "id": 3581, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3582, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3583, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3584, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3585, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 358, - "url": "https://via.placeholder.com/400x300/0d6e75/ffffff?text=ZenGear+Monitor+388P", - "description": "Monitor product image" - } - ], - "created_at": "2024-09-01T09:16:11.471211Z", - "updated_at": "2025-04-24T09:16:11.471211Z" - }, - { - "id": 359, - "name": "NeoCook Monitor 255A", - "description": "NeoCook Monitor 255A is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.84, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1604, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 53.5, - "mass": 2.08, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 46.5, - "mass": 1.38, - "unit": "kg" - } - ], - "components": [ - { - "id": 3591, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3592, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3593, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 359, - "url": "https://via.placeholder.com/400x300/07c03d/ffffff?text=NeoCook+Monitor+255A", - "description": "Monitor product image" - } - ], - "created_at": "2024-12-14T09:16:11.471268Z", - "updated_at": "2025-07-09T09:16:11.471268Z" - }, - { - "id": 360, - "name": "CleanWave Humidifier 538D", - "description": "CleanWave Humidifier 538D is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.23, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1871, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 27.3, - "mass": 0.86, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 6.7, - "mass": 0.17, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 28.0, - "mass": 0.66, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 38.0, - "mass": 1.19, - "unit": "kg" - } - ], - "components": [ - { - "id": 3601, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3602, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 360, - "url": "https://via.placeholder.com/400x300/0e3101/ffffff?text=CleanWave+Humidifier+538D", - "description": "Humidifier product image" - } - ], - "created_at": "2024-11-25T09:16:11.471333Z", - "updated_at": "2025-04-29T09:16:11.471333Z" - }, - { - "id": 361, - "name": "PureLife Monitor 424K", - "description": "PureLife Monitor 424K is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.74, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1874, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 52.7, - "mass": 1.52, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 47.3, - "mass": 0.99, - "unit": "kg" - } - ], - "components": [ - { - "id": 3611, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3612, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 361, - "url": "https://via.placeholder.com/400x300/09916e/ffffff?text=PureLife+Monitor+424K", - "description": "Monitor product image" - } - ], - "created_at": "2024-11-05T09:16:11.471377Z", - "updated_at": "2025-07-01T09:16:11.471377Z" - }, - { - "id": 362, - "name": "CleanWave Rice Cooker 678K", - "description": "CleanWave Rice Cooker 678K is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.67, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1960, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 54.3, - "mass": 1.96, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 17.1, - "mass": 0.63, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 28.6, - "mass": 1.07, - "unit": "kg" - } - ], - "components": [ - { - "id": 3621, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3622, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3623, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3624, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3625, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 362, - "url": "https://via.placeholder.com/400x300/038519/ffffff?text=CleanWave+Rice+Cooker+678K", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-08-31T09:16:11.471443Z", - "updated_at": "2025-06-22T09:16:11.471443Z" - }, - { - "id": 363, - "name": "AquaPro Coffee Maker 546D", - "description": "AquaPro Coffee Maker 546D is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.91, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1654, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 43.8, - "mass": 1.41, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 9.1, - "mass": 0.35, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 9.1, - "mass": 0.29, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 38.0, - "mass": 0.79, - "unit": "kg" - } - ], - "components": [ - { - "id": 3631, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3632, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 363, - "url": "https://via.placeholder.com/400x300/066cc4/ffffff?text=AquaPro+Coffee+Maker+546D", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-05-23T09:16:11.471496Z", - "updated_at": "2025-06-15T09:16:11.471496Z" - }, - { - "id": 364, - "name": "AquaPro Blender 920U", - "description": "AquaPro Blender 920U is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.71, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2069, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 26.5, - "mass": 0.86, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 27.2, - "mass": 0.95, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 16.9, - "mass": 0.37, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 29.4, - "mass": 0.57, - "unit": "kg" - } - ], - "components": [ - { - "id": 3641, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 3642, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 364, - "url": "https://via.placeholder.com/400x300/066b68/ffffff?text=AquaPro+Blender+920U", - "description": "Blender product image" - } - ], - "created_at": "2025-02-28T09:16:11.471553Z", - "updated_at": "2025-07-21T09:16:11.471553Z" - }, - { - "id": 365, - "name": "CleanWave Blender 815R", - "description": "CleanWave Blender 815R is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.14, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2136, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 30.0, - "mass": 0.84, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 27.8, - "mass": 0.31, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 42.2, - "mass": 1.52, - "unit": "kg" - } - ], - "components": [ - { - "id": 3651, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 3652, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 3653, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 3654, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 3655, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 365, - "url": "https://via.placeholder.com/400x300/0c5cff/ffffff?text=CleanWave+Blender+815R", - "description": "Blender product image" - } - ], - "created_at": "2024-11-28T09:16:11.471615Z", - "updated_at": "2025-07-06T09:16:11.471615Z" - }, - { - "id": 366, - "name": "EcoTech Monitor 667L", - "description": "EcoTech Monitor 667L is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.99, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2017, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 64.4, - "mass": 2.18, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 35.6, - "mass": 0.46, - "unit": "kg" - } - ], - "components": [ - { - "id": 3661, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3662, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3663, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3664, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3665, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 366, - "url": "https://via.placeholder.com/400x300/06274f/ffffff?text=EcoTech+Monitor+667L", - "description": "Monitor product image" - } - ], - "created_at": "2024-08-03T09:16:11.471675Z", - "updated_at": "2025-05-25T09:16:11.471675Z" - }, - { - "id": 367, - "name": "ChefMate Monitor 439N", - "description": "ChefMate Monitor 439N is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.54, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1726, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 9.6, - "mass": 0.36, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 17.3, - "mass": 0.46, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 53.8, - "mass": 1.05, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 19.2, - "mass": 0.32, - "unit": "kg" - } - ], - "components": [ - { - "id": 3671, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3672, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3673, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3674, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 367, - "url": "https://via.placeholder.com/400x300/099931/ffffff?text=ChefMate+Monitor+439N", - "description": "Monitor product image" - } - ], - "created_at": "2025-02-28T09:16:11.471733Z", - "updated_at": "2025-07-09T09:16:11.471733Z" - }, - { - "id": 368, - "name": "NeoCook Kettle 142Q", - "description": "NeoCook Kettle 142Q is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.7, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1096, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 39.9, - "mass": 1.17, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 33.6, - "mass": 0.59, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 26.6, - "mass": 0.47, - "unit": "kg" - } - ], - "components": [ - { - "id": 3681, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3682, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3683, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3684, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 368, - "url": "https://via.placeholder.com/400x300/0c4a10/ffffff?text=NeoCook+Kettle+142Q", - "description": "Kettle product image" - } - ], - "created_at": "2024-06-01T09:16:11.471797Z", - "updated_at": "2025-06-07T09:16:11.471797Z" - }, - { - "id": 369, - "name": "NeoCook Coffee Maker 650W", - "description": "NeoCook Coffee Maker 650W is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.74, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2057, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 20.6, - "mass": 0.28, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 39.7, - "mass": 1.33, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 39.7, - "mass": 0.69, - "unit": "kg" - } - ], - "components": [ - { - "id": 3691, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3692, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 369, - "url": "https://via.placeholder.com/400x300/0f0d19/ffffff?text=NeoCook+Coffee+Maker+650W", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-07-02T09:16:11.471846Z", - "updated_at": "2025-05-22T09:16:11.471846Z" - }, - { - "id": 370, - "name": "SmartHome Air Purifier 272A", - "description": "SmartHome Air Purifier 272A is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.79, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1877, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 35.7, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 39.7, - "mass": 1.05, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 9.5, - "mass": 0.27, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 15.1, - "mass": 0.55, - "unit": "kg" - } - ], - "components": [ - { - "id": 3701, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 3702, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 3703, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 3704, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 370, - "url": "https://via.placeholder.com/400x300/071d71/ffffff?text=SmartHome+Air+Purifier+272A", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-10-06T09:16:11.471966Z", - "updated_at": "2025-05-21T09:16:11.471966Z" - }, - { - "id": 371, - "name": "ChefMate Fan 167R", - "description": "ChefMate Fan 167R is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.21, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2157, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 8.5, - "mass": 0.23, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 33.9, - "mass": 1.01, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 33.1, - "mass": 0.92, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 24.6, - "mass": 0.87, - "unit": "kg" - } - ], - "components": [ - { - "id": 3711, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 3712, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 3713, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 3714, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - }, - { - "id": 3715, - "name": "Fan Component 5", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 371, - "url": "https://via.placeholder.com/400x300/0b339f/ffffff?text=ChefMate+Fan+167R", - "description": "Fan product image" - } - ], - "created_at": "2024-10-28T09:16:11.472033Z", - "updated_at": "2025-06-05T09:16:11.472033Z" - }, - { - "id": 372, - "name": "EcoTech Humidifier 695R", - "description": "EcoTech Humidifier 695R is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.96, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2083, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 23.1, - "mass": 0.74, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 76.9, - "mass": 1.74, - "unit": "kg" - } - ], - "components": [ - { - "id": 3721, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3722, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 372, - "url": "https://via.placeholder.com/400x300/01f9d2/ffffff?text=EcoTech+Humidifier+695R", - "description": "Humidifier product image" - } - ], - "created_at": "2025-02-15T09:16:11.472084Z", - "updated_at": "2025-06-02T09:16:11.472084Z" - }, - { - "id": 373, - "name": "ZenGear Kettle 958R", - "description": "ZenGear Kettle 958R is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.67, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2123, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 10.4, - "mass": 0.25, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 33.9, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 22.6, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 33.0, - "mass": 1.03, - "unit": "kg" - } - ], - "components": [ - { - "id": 3731, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3732, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3733, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3734, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 373, - "url": "https://via.placeholder.com/400x300/0ab909/ffffff?text=ZenGear+Kettle+958R", - "description": "Kettle product image" - } - ], - "created_at": "2025-01-02T09:16:11.472136Z", - "updated_at": "2025-04-24T09:16:11.472136Z" - }, - { - "id": 374, - "name": "SmartHome Iron 659S", - "description": "SmartHome Iron 659S is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.73, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1523, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 59.1, - "mass": 1.08, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 40.9, - "mass": 0.42, - "unit": "kg" - } - ], - "components": [ - { - "id": 3741, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 3742, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 3743, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 3744, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - }, - { - "id": 3745, - "name": "Iron Component 5", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 374, - "url": "https://via.placeholder.com/400x300/0503bf/ffffff?text=SmartHome+Iron+659S", - "description": "Iron product image" - } - ], - "created_at": "2025-01-23T09:16:11.472197Z", - "updated_at": "2025-05-30T09:16:11.472197Z" - }, - { - "id": 375, - "name": "PureLife Iron 683G", - "description": "PureLife Iron 683G is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.72, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2034, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 36.5, - "mass": 1.3, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 63.5, - "mass": 1.51, - "unit": "kg" - } - ], - "components": [ - { - "id": 3751, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 3752, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 375, - "url": "https://via.placeholder.com/400x300/063462/ffffff?text=PureLife+Iron+683G", - "description": "Iron product image" - } - ], - "created_at": "2024-11-09T09:16:11.472251Z", - "updated_at": "2025-05-24T09:16:11.472251Z" - }, - { - "id": 376, - "name": "SmartHome Air Purifier 358B", - "description": "SmartHome Air Purifier 358B is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.99, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1995, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 50.0, - "mass": 1.24, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 50.0, - "mass": 1.76, - "unit": "kg" - } - ], - "components": [ - { - "id": 3761, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 3762, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 3763, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 376, - "url": "https://via.placeholder.com/400x300/09c5e0/ffffff?text=SmartHome+Air+Purifier+358B", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-09-01T09:16:11.472304Z", - "updated_at": "2025-06-26T09:16:11.472304Z" - }, - { - "id": 377, - "name": "ZenGear Kettle 126C", - "description": "ZenGear Kettle 126C is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.87, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1718, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 57.1, - "mass": 1.46, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 24.8, - "mass": 0.5, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 18.1, - "mass": 0.2, - "unit": "kg" - } - ], - "components": [ - { - "id": 3771, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3772, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3773, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 377, - "url": "https://via.placeholder.com/400x300/08d421/ffffff?text=ZenGear+Kettle+126C", - "description": "Kettle product image" - } - ], - "created_at": "2024-04-06T09:16:11.472370Z", - "updated_at": "2025-07-02T09:16:11.472370Z" - }, - { - "id": 378, - "name": "SmartHome Blender 357W", - "description": "SmartHome Blender 357W is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.91, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1383, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 16.0, - "mass": 0.64, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 57.4, - "mass": 2.22, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 26.6, - "mass": 0.87, - "unit": "kg" - } - ], - "components": [ - { - "id": 3781, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 3782, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 3783, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 3784, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 378, - "url": "https://via.placeholder.com/400x300/0c34c2/ffffff?text=SmartHome+Blender+357W", - "description": "Blender product image" - } - ], - "created_at": "2024-07-27T09:16:11.472426Z", - "updated_at": "2025-04-25T09:16:11.472426Z" - }, - { - "id": 379, - "name": "AquaPro Coffee Maker 348Z", - "description": "AquaPro Coffee Maker 348Z is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.79, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1153, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 33.8, - "mass": 0.73, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 66.2, - "mass": 1.81, - "unit": "kg" - } - ], - "components": [ - { - "id": 3791, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3792, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3793, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3794, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 379, - "url": "https://via.placeholder.com/400x300/051b95/ffffff?text=AquaPro+Coffee+Maker+348Z", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-12-15T09:16:11.472483Z", - "updated_at": "2025-05-07T09:16:11.472483Z" - }, - { - "id": 380, - "name": "NeoCook Humidifier 197O", - "description": "NeoCook Humidifier 197O is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.02, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1534, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 32.7, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 36.0, - "mass": 0.89, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 18.0, - "mass": 0.62, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 13.3, - "mass": 0.2, - "unit": "kg" - } - ], - "components": [ - { - "id": 3801, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3802, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3803, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3804, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 380, - "url": "https://via.placeholder.com/400x300/0e9df9/ffffff?text=NeoCook+Humidifier+197O", - "description": "Humidifier product image" - } - ], - "created_at": "2024-12-07T09:16:11.472543Z", - "updated_at": "2025-07-03T09:16:11.472543Z" - }, - { - "id": 381, - "name": "NeoCook Iron 597T", - "description": "NeoCook Iron 597T is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.61, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 925, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 43.2, - "mass": 0.82, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 44.0, - "mass": 1.49, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 12.8, - "mass": 0.42, - "unit": "kg" - } - ], - "components": [ - { - "id": 3811, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 3812, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 3813, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 381, - "url": "https://via.placeholder.com/400x300/0a5cca/ffffff?text=NeoCook+Iron+597T", - "description": "Iron product image" - } - ], - "created_at": "2024-07-15T09:16:11.472600Z", - "updated_at": "2025-06-25T09:16:11.472600Z" - }, - { - "id": 382, - "name": "AquaPro Toaster 280U", - "description": "AquaPro Toaster 280U is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.9, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1366, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 34.1, - "mass": 1.22, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 21.4, - "mass": 0.83, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 44.4, - "mass": 1.35, - "unit": "kg" - } - ], - "components": [ - { - "id": 3821, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3822, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3823, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3824, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 382, - "url": "https://via.placeholder.com/400x300/0db2e3/ffffff?text=AquaPro+Toaster+280U", - "description": "Toaster product image" - } - ], - "created_at": "2024-12-08T09:16:11.472654Z", - "updated_at": "2025-07-31T09:16:11.472654Z" - }, - { - "id": 383, - "name": "NeoCook Monitor 940X", - "description": "NeoCook Monitor 940X is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.66, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2049, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 22.3, - "mass": 0.75, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 35.1, - "mass": 0.41, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 42.6, - "mass": 1.2, - "unit": "kg" - } - ], - "components": [ - { - "id": 3831, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3832, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3833, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3834, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 383, - "url": "https://via.placeholder.com/400x300/0c1ffc/ffffff?text=NeoCook+Monitor+940X", - "description": "Monitor product image" - } - ], - "created_at": "2024-08-02T09:16:11.472717Z", - "updated_at": "2025-04-28T09:16:11.472717Z" - }, - { - "id": 384, - "name": "NeoCook Coffee Maker 940C", - "description": "NeoCook Coffee Maker 940C is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.41, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1729, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 40.0, - "mass": 1.34, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 39.0, - "mass": 1.17, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 21.0, - "mass": 0.79, - "unit": "kg" - } - ], - "components": [ - { - "id": 3841, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3842, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3843, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3844, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3845, - "name": "Coffee Maker Component 5", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 384, - "url": "https://via.placeholder.com/400x300/03ccf6/ffffff?text=NeoCook+Coffee+Maker+940C", - "description": "Coffee Maker product image" - } - ], - "created_at": "2025-04-05T09:16:11.472782Z", - "updated_at": "2025-07-25T09:16:11.472782Z" - }, - { - "id": 385, - "name": "NeoCook Blender 767P", - "description": "NeoCook Blender 767P is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.58, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1909, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 51.5, - "mass": 2.01, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 20.4, - "mass": 0.28, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 28.2, - "mass": 1.05, - "unit": "kg" - } - ], - "components": [ - { - "id": 3851, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 3852, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 3853, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 3854, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 3855, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 385, - "url": "https://via.placeholder.com/400x300/0ae109/ffffff?text=NeoCook+Blender+767P", - "description": "Blender product image" - } - ], - "created_at": "2025-01-18T09:16:11.472846Z", - "updated_at": "2025-06-17T09:16:11.472846Z" - }, - { - "id": 386, - "name": "PureLife Fan 397H", - "description": "PureLife Fan 397H is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.35, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1268, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 23.6, - "mass": 0.83, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 27.8, - "mass": 0.41, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 48.6, - "mass": 0.53, - "unit": "kg" - } - ], - "components": [ - { - "id": 3861, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 3862, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 3863, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 3864, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 386, - "url": "https://via.placeholder.com/400x300/0d571e/ffffff?text=PureLife+Fan+397H", - "description": "Fan product image" - } - ], - "created_at": "2024-09-30T09:16:11.472900Z", - "updated_at": "2025-07-10T09:16:11.472900Z" - }, - { - "id": 387, - "name": "SmartHome Coffee Maker 502Q", - "description": "SmartHome Coffee Maker 502Q is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.27, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1794, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 40.5, - "mass": 0.98, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 9.5, - "mass": 0.22, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 34.5, - "mass": 0.63, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 15.5, - "mass": 0.2, - "unit": "kg" - } - ], - "components": [ - { - "id": 3871, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3872, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 3873, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 387, - "url": "https://via.placeholder.com/400x300/061a97/ffffff?text=SmartHome+Coffee+Maker+502Q", - "description": "Coffee Maker product image" - } - ], - "created_at": "2025-03-28T09:16:11.472959Z", - "updated_at": "2025-07-11T09:16:11.472959Z" - }, - { - "id": 388, - "name": "SmartHome Humidifier 753A", - "description": "SmartHome Humidifier 753A is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.31, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 30.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2195, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 27.8, - "mass": 0.59, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 24.7, - "mass": 0.83, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 20.4, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 27.2, - "mass": 0.92, - "unit": "kg" - } - ], - "components": [ - { - "id": 3881, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3882, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3883, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3884, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 388, - "url": "https://via.placeholder.com/400x300/0a9019/ffffff?text=SmartHome+Humidifier+753A", - "description": "Humidifier product image" - } - ], - "created_at": "2024-07-02T09:16:11.473018Z", - "updated_at": "2025-07-14T09:16:11.473018Z" - }, - { - "id": 389, - "name": "ChefMate Rice Cooker 395K", - "description": "ChefMate Rice Cooker 395K is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.35, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1871, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 52.8, - "mass": 0.53, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 47.2, - "mass": 1.43, - "unit": "kg" - } - ], - "components": [ - { - "id": 3891, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3892, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3893, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 3894, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 389, - "url": "https://via.placeholder.com/400x300/0adc29/ffffff?text=ChefMate+Rice+Cooker+395K", - "description": "Rice Cooker product image" - } - ], - "created_at": "2025-04-17T09:16:11.473086Z", - "updated_at": "2025-06-27T09:16:11.473086Z" - }, - { - "id": 390, - "name": "EcoTech Iron 792K", - "description": "EcoTech Iron 792K is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.73, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1683, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 77.6, - "mass": 1.25, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 22.4, - "mass": 0.29, - "unit": "kg" - } - ], - "components": [ - { - "id": 3901, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 3902, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 3903, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 390, - "url": "https://via.placeholder.com/400x300/02f7a7/ffffff?text=EcoTech+Iron+792K", - "description": "Iron product image" - } - ], - "created_at": "2024-06-14T09:16:11.473133Z", - "updated_at": "2025-05-16T09:16:11.473133Z" - }, - { - "id": 391, - "name": "CleanWave Kettle 221G", - "description": "CleanWave Kettle 221G is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.68, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1875, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 48.8, - "mass": 0.92, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 51.2, - "mass": 0.61, - "unit": "kg" - } - ], - "components": [ - { - "id": 3911, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3912, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3913, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 391, - "url": "https://via.placeholder.com/400x300/07f59d/ffffff?text=CleanWave+Kettle+221G", - "description": "Kettle product image" - } - ], - "created_at": "2024-05-31T09:16:11.473184Z", - "updated_at": "2025-07-12T09:16:11.473184Z" - }, - { - "id": 392, - "name": "SmartHome Air Purifier 987Y", - "description": "SmartHome Air Purifier 987Y is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.7, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1843, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 48.6, - "mass": 1.82, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 51.4, - "mass": 1.48, - "unit": "kg" - } - ], - "components": [ - { - "id": 3921, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 3922, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 3923, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 3924, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 392, - "url": "https://via.placeholder.com/400x300/0b5a99/ffffff?text=SmartHome+Air+Purifier+987Y", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-12-17T09:16:11.473240Z", - "updated_at": "2025-05-15T09:16:11.473240Z" - }, - { - "id": 393, - "name": "EcoTech Blender 261Q", - "description": "EcoTech Blender 261Q is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.51, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2061, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 30.2, - "mass": 1.19, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 30.9, - "mass": 0.63, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 21.6, - "mass": 0.58, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 17.3, - "mass": 0.68, - "unit": "kg" - } - ], - "components": [ - { - "id": 3931, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 3932, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 3933, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 3934, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 393, - "url": "https://via.placeholder.com/400x300/0a62ec/ffffff?text=EcoTech+Blender+261Q", - "description": "Blender product image" - } - ], - "created_at": "2025-03-27T09:16:11.473313Z", - "updated_at": "2025-07-30T09:16:11.473313Z" - }, - { - "id": 394, - "name": "ZenGear Monitor 970W", - "description": "ZenGear Monitor 970W is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.96, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2050, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 29.3, - "mass": 0.91, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 35.9, - "mass": 0.87, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 34.7, - "mass": 1.02, - "unit": "kg" - } - ], - "components": [ - { - "id": 3941, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3942, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 3943, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 394, - "url": "https://via.placeholder.com/400x300/0deb64/ffffff?text=ZenGear+Monitor+970W", - "description": "Monitor product image" - } - ], - "created_at": "2024-06-04T09:16:11.473363Z", - "updated_at": "2025-07-29T09:16:11.473363Z" - }, - { - "id": 395, - "name": "ZenGear Toaster 160N", - "description": "ZenGear Toaster 160N is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.8, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2051, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 15.7, - "mass": 0.59, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 84.3, - "mass": 2.15, - "unit": "kg" - } - ], - "components": [ - { - "id": 3951, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 3952, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 395, - "url": "https://via.placeholder.com/400x300/03488f/ffffff?text=ZenGear+Toaster+160N", - "description": "Toaster product image" - } - ], - "created_at": "2025-03-29T09:16:11.473417Z", - "updated_at": "2025-07-12T09:16:11.473417Z" - }, - { - "id": 396, - "name": "CleanWave Kettle 771D", - "description": "CleanWave Kettle 771D is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.15, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1268, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 15.1, - "mass": 0.34, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 17.8, - "mass": 0.49, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 38.8, - "mass": 1.02, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 28.3, - "mass": 0.66, - "unit": "kg" - } - ], - "components": [ - { - "id": 3961, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3962, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3963, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 3964, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 396, - "url": "https://via.placeholder.com/400x300/093dad/ffffff?text=CleanWave+Kettle+771D", - "description": "Kettle product image" - } - ], - "created_at": "2024-07-13T09:16:11.473487Z", - "updated_at": "2025-06-26T09:16:11.473487Z" - }, - { - "id": 397, - "name": "ZenGear Fan 286A", - "description": "ZenGear Fan 286A is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.26, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 865, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 49.1, - "mass": 0.73, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 18.4, - "mass": 0.68, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 32.5, - "mass": 1.19, - "unit": "kg" - } - ], - "components": [ - { - "id": 3971, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 3972, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 3973, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 3974, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 397, - "url": "https://via.placeholder.com/400x300/0e9894/ffffff?text=ZenGear+Fan+286A", - "description": "Fan product image" - } - ], - "created_at": "2024-05-28T09:16:11.473541Z", - "updated_at": "2025-06-03T09:16:11.473541Z" - }, - { - "id": 398, - "name": "AquaPro Humidifier 521X", - "description": "AquaPro Humidifier 521X is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.64, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1186, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 10.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 9.1, - "mass": 0.28, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 43.6, - "mass": 0.8, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 20.0, - "mass": 0.51, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 27.3, - "mass": 0.32, - "unit": "kg" - } - ], - "components": [ - { - "id": 3981, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3982, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 398, - "url": "https://via.placeholder.com/400x300/0c8502/ffffff?text=AquaPro+Humidifier+521X", - "description": "Humidifier product image" - } - ], - "created_at": "2024-04-02T09:16:11.473597Z", - "updated_at": "2025-05-03T09:16:11.473597Z" - }, - { - "id": 399, - "name": "CleanWave Humidifier 631V", - "description": "CleanWave Humidifier 631V is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.88, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 866, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 33.8, - "mass": 1.04, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 36.2, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 30.0, - "mass": 0.7, - "unit": "kg" - } - ], - "components": [ - { - "id": 3991, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3992, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3993, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3994, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 3995, - "name": "Humidifier Component 5", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 399, - "url": "https://via.placeholder.com/400x300/0b1fba/ffffff?text=CleanWave+Humidifier+631V", - "description": "Humidifier product image" - } - ], - "created_at": "2025-03-02T09:16:11.473655Z", - "updated_at": "2025-07-24T09:16:11.473655Z" - }, - { - "id": 400, - "name": "AquaPro Humidifier 657I", - "description": "AquaPro Humidifier 657I is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.06, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1684, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 49.0, - "mass": 1.23, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 16.0, - "mass": 0.5, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 35.0, - "mass": 1.08, - "unit": "kg" - } - ], - "components": [ - { - "id": 4001, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4002, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4003, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 400, - "url": "https://via.placeholder.com/400x300/060be2/ffffff?text=AquaPro+Humidifier+657I", - "description": "Humidifier product image" - } - ], - "created_at": "2025-01-22T09:16:11.473711Z", - "updated_at": "2025-07-07T09:16:11.473711Z" - }, - { - "id": 401, - "name": "PureLife Monitor 309E", - "description": "PureLife Monitor 309E is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.32, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1857, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 60.3, - "mass": 1.55, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 39.7, - "mass": 0.97, - "unit": "kg" - } - ], - "components": [ - { - "id": 4011, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 4012, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 4013, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 401, - "url": "https://via.placeholder.com/400x300/068e9e/ffffff?text=PureLife+Monitor+309E", - "description": "Monitor product image" - } - ], - "created_at": "2024-06-10T09:16:11.473764Z", - "updated_at": "2025-05-09T09:16:11.473764Z" - }, - { - "id": 402, - "name": "ZenGear Blender 708U", - "description": "ZenGear Blender 708U is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.9, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1547, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 54.0, - "mass": 1.9, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 46.0, - "mass": 0.51, - "unit": "kg" - } - ], - "components": [ - { - "id": 4021, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 4022, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 4023, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 402, - "url": "https://via.placeholder.com/400x300/0458c7/ffffff?text=ZenGear+Blender+708U", - "description": "Blender product image" - } - ], - "created_at": "2025-02-09T09:16:11.473826Z", - "updated_at": "2025-05-21T09:16:11.473826Z" - }, - { - "id": 403, - "name": "ZenGear Kettle 161D", - "description": "ZenGear Kettle 161D is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.2, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1171, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 38.6, - "mass": 0.52, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 8.5, - "mass": 0.29, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 18.3, - "mass": 0.21, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 34.6, - "mass": 0.89, - "unit": "kg" - } - ], - "components": [ - { - "id": 4031, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4032, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 403, - "url": "https://via.placeholder.com/400x300/0d02aa/ffffff?text=ZenGear+Kettle+161D", - "description": "Kettle product image" - } - ], - "created_at": "2024-12-18T09:16:11.473884Z", - "updated_at": "2025-07-12T09:16:11.473884Z" - }, - { - "id": 404, - "name": "PureLife Monitor 924P", - "description": "PureLife Monitor 924P is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.18, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1457, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 28.6, - "mass": 0.44, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 28.6, - "mass": 0.57, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 42.9, - "mass": 1.16, - "unit": "kg" - } - ], - "components": [ - { - "id": 4041, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 4042, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 4043, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 4044, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - }, - { - "id": 4045, - "name": "Monitor Component 5", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 404, - "url": "https://via.placeholder.com/400x300/080e63/ffffff?text=PureLife+Monitor+924P", - "description": "Monitor product image" - } - ], - "created_at": "2025-01-31T09:16:11.474008Z", - "updated_at": "2025-05-03T09:16:11.474008Z" - }, - { - "id": 405, - "name": "CleanWave Blender 195Y", - "description": "CleanWave Blender 195Y is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.97, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1167, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 52.5, - "mass": 0.9, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 47.5, - "mass": 1.16, - "unit": "kg" - } - ], - "components": [ - { - "id": 4051, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 4052, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 4053, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 4054, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 405, - "url": "https://via.placeholder.com/400x300/0edf63/ffffff?text=CleanWave+Blender+195Y", - "description": "Blender product image" - } - ], - "created_at": "2025-03-31T09:16:11.474063Z", - "updated_at": "2025-07-21T09:16:11.474063Z" - }, - { - "id": 406, - "name": "PureLife Coffee Maker 549B", - "description": "PureLife Coffee Maker 549B is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.05, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 24.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1923, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 42.3, - "mass": 1.24, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 34.5, - "mass": 0.81, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 16.2, - "mass": 0.44, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 7.0, - "mass": 0.22, - "unit": "kg" - } - ], - "components": [ - { - "id": 4061, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4062, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 406, - "url": "https://via.placeholder.com/400x300/0d136f/ffffff?text=PureLife+Coffee+Maker+549B", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-05-21T09:16:11.474123Z", - "updated_at": "2025-07-10T09:16:11.474123Z" - }, - { - "id": 407, - "name": "SmartHome Toaster 629M", - "description": "SmartHome Toaster 629M is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.64, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 834, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 38.8, - "mass": 1.37, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 61.2, - "mass": 1.66, - "unit": "kg" - } - ], - "components": [ - { - "id": 4071, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4072, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4073, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4074, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 407, - "url": "https://via.placeholder.com/400x300/0389a8/ffffff?text=SmartHome+Toaster+629M", - "description": "Toaster product image" - } - ], - "created_at": "2024-12-01T09:16:11.474179Z", - "updated_at": "2025-06-15T09:16:11.474179Z" - }, - { - "id": 408, - "name": "PureLife Monitor 231I", - "description": "PureLife Monitor 231I is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.89, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1726, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 53.1, - "mass": 2.0, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 46.9, - "mass": 1.08, - "unit": "kg" - } - ], - "components": [ - { - "id": 4081, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 4082, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 4083, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - }, - { - "id": 4084, - "name": "Monitor Component 4", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 408, - "url": "https://via.placeholder.com/400x300/02248f/ffffff?text=PureLife+Monitor+231I", - "description": "Monitor product image" - } - ], - "created_at": "2024-03-28T09:16:11.474233Z", - "updated_at": "2025-06-17T09:16:11.474233Z" - }, - { - "id": 409, - "name": "ZenGear Fan 109Y", - "description": "ZenGear Fan 109Y is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.37, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1380, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 32.9, - "mass": 0.7, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 67.1, - "mass": 2.06, - "unit": "kg" - } - ], - "components": [ - { - "id": 4091, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 4092, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 4093, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 4094, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - }, - { - "id": 4095, - "name": "Fan Component 5", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 409, - "url": "https://via.placeholder.com/400x300/04d8bc/ffffff?text=ZenGear+Fan+109Y", - "description": "Fan product image" - } - ], - "created_at": "2025-04-12T09:16:11.474273Z", - "updated_at": "2025-07-02T09:16:11.474273Z" - }, - { - "id": 410, - "name": "EcoTech Toaster 705C", - "description": "EcoTech Toaster 705C is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.81, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1207, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 24.3, - "mass": 0.29, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 20.1, - "mass": 0.67, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 18.8, - "mass": 0.64, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 36.8, - "mass": 1.2, - "unit": "kg" - } - ], - "components": [ - { - "id": 4101, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4102, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4103, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4104, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4105, - "name": "Toaster Component 5", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 410, - "url": "https://via.placeholder.com/400x300/04bf1f/ffffff?text=EcoTech+Toaster+705C", - "description": "Toaster product image" - } - ], - "created_at": "2024-07-28T09:16:11.474331Z", - "updated_at": "2025-05-27T09:16:11.474331Z" - }, - { - "id": 411, - "name": "AquaPro Kettle 901D", - "description": "AquaPro Kettle 901D is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.97, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1922, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 11.2, - "mass": 0.24, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 14.7, - "mass": 0.49, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 32.8, - "mass": 0.5, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 41.4, - "mass": 1.2, - "unit": "kg" - } - ], - "components": [ - { - "id": 4111, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4112, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4113, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4114, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4115, - "name": "Kettle Component 5", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 411, - "url": "https://via.placeholder.com/400x300/0b8b33/ffffff?text=AquaPro+Kettle+901D", - "description": "Kettle product image" - } - ], - "created_at": "2024-03-26T09:16:11.474388Z", - "updated_at": "2025-07-09T09:16:11.474388Z" - }, - { - "id": 412, - "name": "SmartHome Toaster 310E", - "description": "SmartHome Toaster 310E is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.78, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 855, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 51.7, - "mass": 1.13, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 48.3, - "mass": 1.22, - "unit": "kg" - } - ], - "components": [ - { - "id": 4121, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4122, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4123, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4124, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 412, - "url": "https://via.placeholder.com/400x300/0d4db9/ffffff?text=SmartHome+Toaster+310E", - "description": "Toaster product image" - } - ], - "created_at": "2024-10-30T09:16:11.474434Z", - "updated_at": "2025-07-21T09:16:11.474434Z" - }, - { - "id": 413, - "name": "EcoTech Iron 639I", - "description": "EcoTech Iron 639I is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.61, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1597, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 30.4, - "mass": 1.11, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 25.5, - "mass": 0.83, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 44.1, - "mass": 1.37, - "unit": "kg" - } - ], - "components": [ - { - "id": 4131, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 4132, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 4133, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 4134, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - }, - { - "id": 4135, - "name": "Iron Component 5", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 413, - "url": "https://via.placeholder.com/400x300/06bb74/ffffff?text=EcoTech+Iron+639I", - "description": "Iron product image" - } - ], - "created_at": "2024-09-06T09:16:11.474511Z", - "updated_at": "2025-05-03T09:16:11.474511Z" - }, - { - "id": 414, - "name": "ZenGear Iron 435D", - "description": "ZenGear Iron 435D is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.88, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1253, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 30.6, - "mass": 0.93, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 20.9, - "mass": 0.6, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 21.9, - "mass": 0.8, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 26.5, - "mass": 0.82, - "unit": "kg" - } - ], - "components": [ - { - "id": 4141, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 4142, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 414, - "url": "https://via.placeholder.com/400x300/02cc71/ffffff?text=ZenGear+Iron+435D", - "description": "Iron product image" - } - ], - "created_at": "2024-11-02T09:16:11.474567Z", - "updated_at": "2025-07-27T09:16:11.474567Z" - }, - { - "id": 415, - "name": "EcoTech Coffee Maker 881G", - "description": "EcoTech Coffee Maker 881G is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.38, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2149, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 40.8, - "mass": 0.66, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 59.2, - "mass": 1.91, - "unit": "kg" - } - ], - "components": [ - { - "id": 4151, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4152, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4153, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 415, - "url": "https://via.placeholder.com/400x300/0c44f4/ffffff?text=EcoTech+Coffee+Maker+881G", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-12-18T09:16:11.474605Z", - "updated_at": "2025-06-12T09:16:11.474605Z" - }, - { - "id": 416, - "name": "PureLife Blender 214I", - "description": "PureLife Blender 214I is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.86, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1465, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 31.1, - "mass": 1.21, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 23.2, - "mass": 0.66, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 37.1, - "mass": 0.39, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 8.6, - "mass": 0.14, - "unit": "kg" - } - ], - "components": [ - { - "id": 4161, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 4162, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 4163, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 4164, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 4165, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 416, - "url": "https://via.placeholder.com/400x300/064489/ffffff?text=PureLife+Blender+214I", - "description": "Blender product image" - } - ], - "created_at": "2024-09-22T09:16:11.474677Z", - "updated_at": "2025-05-30T09:16:11.474677Z" - }, - { - "id": 417, - "name": "ChefMate Monitor 611R", - "description": "ChefMate Monitor 611R is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.56, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1280, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 29.5, - "mass": 0.92, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 70.5, - "mass": 0.78, - "unit": "kg" - } - ], - "components": [ - { - "id": 4171, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 4172, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 417, - "url": "https://via.placeholder.com/400x300/0e1aee/ffffff?text=ChefMate+Monitor+611R", - "description": "Monitor product image" - } - ], - "created_at": "2024-06-25T09:16:11.474722Z", - "updated_at": "2025-05-08T09:16:11.474722Z" - }, - { - "id": 418, - "name": "ChefMate Air Purifier 787V", - "description": "ChefMate Air Purifier 787V is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.05, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2059, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 13.7, - "mass": 0.43, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 53.7, - "mass": 1.11, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 32.6, - "mass": 0.83, - "unit": "kg" - } - ], - "components": [ - { - "id": 4181, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4182, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4183, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 418, - "url": "https://via.placeholder.com/400x300/0aaca8/ffffff?text=ChefMate+Air+Purifier+787V", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-09-02T09:16:11.474769Z", - "updated_at": "2025-07-06T09:16:11.474769Z" - }, - { - "id": 419, - "name": "EcoTech Kettle 870P", - "description": "EcoTech Kettle 870P is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.69, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2120, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 25.4, - "mass": 0.56, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 74.6, - "mass": 2.85, - "unit": "kg" - } - ], - "components": [ - { - "id": 4191, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4192, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4193, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4194, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4195, - "name": "Kettle Component 5", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 419, - "url": "https://via.placeholder.com/400x300/08a405/ffffff?text=EcoTech+Kettle+870P", - "description": "Kettle product image" - } - ], - "created_at": "2024-07-24T09:16:11.474814Z", - "updated_at": "2025-07-27T09:16:11.474814Z" - }, - { - "id": 420, - "name": "EcoTech Rice Cooker 467K", - "description": "EcoTech Rice Cooker 467K is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.78, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 30.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1972, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 71.1, - "mass": 2.49, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 28.9, - "mass": 0.45, - "unit": "kg" - } - ], - "components": [ - { - "id": 4201, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4202, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4203, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 420, - "url": "https://via.placeholder.com/400x300/0cc027/ffffff?text=EcoTech+Rice+Cooker+467K", - "description": "Rice Cooker product image" - } - ], - "created_at": "2025-01-10T09:16:11.474879Z", - "updated_at": "2025-06-23T09:16:11.474879Z" - }, - { - "id": 421, - "name": "PureLife Rice Cooker 120N", - "description": "PureLife Rice Cooker 120N is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.86, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1374, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 54.1, - "mass": 1.58, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 45.9, - "mass": 0.78, - "unit": "kg" - } - ], - "components": [ - { - "id": 4211, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4212, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4213, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 421, - "url": "https://via.placeholder.com/400x300/0670be/ffffff?text=PureLife+Rice+Cooker+120N", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-12-15T09:16:11.474921Z", - "updated_at": "2025-06-02T09:16:11.474921Z" - }, - { - "id": 422, - "name": "PureLife Humidifier 155E", - "description": "PureLife Humidifier 155E is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.55, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1766, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 34.4, - "mass": 1.32, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 36.8, - "mass": 0.71, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 28.8, - "mass": 0.75, - "unit": "kg" - } - ], - "components": [ - { - "id": 4221, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4222, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4223, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4224, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4225, - "name": "Humidifier Component 5", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 422, - "url": "https://via.placeholder.com/400x300/06a751/ffffff?text=PureLife+Humidifier+155E", - "description": "Humidifier product image" - } - ], - "created_at": "2024-07-31T09:16:11.474964Z", - "updated_at": "2025-07-09T09:16:11.474964Z" - }, - { - "id": 423, - "name": "EcoTech Kettle 934T", - "description": "EcoTech Kettle 934T is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.63, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2062, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 10.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 19.0, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 20.4, - "mass": 0.66, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 16.8, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 43.8, - "mass": 0.67, - "unit": "kg" - } - ], - "components": [ - { - "id": 4231, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4232, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4233, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4234, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 423, - "url": "https://via.placeholder.com/400x300/091036/ffffff?text=EcoTech+Kettle+934T", - "description": "Kettle product image" - } - ], - "created_at": "2024-06-03T09:16:11.475197Z", - "updated_at": "2025-07-31T09:16:11.475197Z" - }, - { - "id": 424, - "name": "ChefMate Toaster 365Y", - "description": "ChefMate Toaster 365Y is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.96, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 19.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1283, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 49.5, - "mass": 1.8, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 50.5, - "mass": 1.1, - "unit": "kg" - } - ], - "components": [ - { - "id": 4241, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4242, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4243, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4244, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 424, - "url": "https://via.placeholder.com/400x300/0545fb/ffffff?text=ChefMate+Toaster+365Y", - "description": "Toaster product image" - } - ], - "created_at": "2024-04-28T09:16:11.475317Z", - "updated_at": "2025-07-23T09:16:11.475317Z" - }, - { - "id": 425, - "name": "EcoTech Iron 187U", - "description": "EcoTech Iron 187U is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.35, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1755, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 16.2, - "mass": 0.25, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 31.3, - "mass": 0.77, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 19.0, - "mass": 0.38, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 33.5, - "mass": 0.93, - "unit": "kg" - } - ], - "components": [ - { - "id": 4251, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 4252, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 4253, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 425, - "url": "https://via.placeholder.com/400x300/0bb191/ffffff?text=EcoTech+Iron+187U", - "description": "Iron product image" - } - ], - "created_at": "2024-09-11T09:16:11.475399Z", - "updated_at": "2025-07-29T09:16:11.475399Z" - }, - { - "id": 426, - "name": "ZenGear Air Purifier 987P", - "description": "ZenGear Air Purifier 987P is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.16, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 944, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 28.7, - "mass": 0.91, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 17.6, - "mass": 0.54, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 15.4, - "mass": 0.26, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 38.2, - "mass": 0.45, - "unit": "kg" - } - ], - "components": [ - { - "id": 4261, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4262, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4263, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4264, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4265, - "name": "Air Purifier Component 5", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 426, - "url": "https://via.placeholder.com/400x300/02cd55/ffffff?text=ZenGear+Air+Purifier+987P", - "description": "Air Purifier product image" - } - ], - "created_at": "2025-02-27T09:16:11.475469Z", - "updated_at": "2025-07-12T09:16:11.475469Z" - }, - { - "id": 427, - "name": "EcoTech Rice Cooker 648O", - "description": "EcoTech Rice Cooker 648O is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.16, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1747, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 51.7, - "mass": 1.88, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 48.3, - "mass": 1.68, - "unit": "kg" - } - ], - "components": [ - { - "id": 4271, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4272, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4273, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4274, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4275, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 427, - "url": "https://via.placeholder.com/400x300/0f147d/ffffff?text=EcoTech+Rice+Cooker+648O", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-11-27T09:16:11.475521Z", - "updated_at": "2025-04-30T09:16:11.475521Z" - }, - { - "id": 428, - "name": "CleanWave Air Purifier 135N", - "description": "CleanWave Air Purifier 135N is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.68, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1657, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 19.8, - "mass": 0.69, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 50.9, - "mass": 1.34, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 29.3, - "mass": 0.59, - "unit": "kg" - } - ], - "components": [ - { - "id": 4281, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4282, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4283, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4284, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4285, - "name": "Air Purifier Component 5", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 428, - "url": "https://via.placeholder.com/400x300/01b0fc/ffffff?text=CleanWave+Air+Purifier+135N", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-12-13T09:16:11.475569Z", - "updated_at": "2025-05-03T09:16:11.475569Z" - }, - { - "id": 429, - "name": "ChefMate Fan 457U", - "description": "ChefMate Fan 457U is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.16, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1623, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 14.7, - "mass": 0.29, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 29.3, - "mass": 0.61, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 20.0, - "mass": 0.71, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 36.0, - "mass": 0.42, - "unit": "kg" - } - ], - "components": [ - { - "id": 4291, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 4292, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 4293, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 4294, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - }, - { - "id": 4295, - "name": "Fan Component 5", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 429, - "url": "https://via.placeholder.com/400x300/08100e/ffffff?text=ChefMate+Fan+457U", - "description": "Fan product image" - } - ], - "created_at": "2025-02-25T09:16:11.475633Z", - "updated_at": "2025-06-23T09:16:11.475633Z" - }, - { - "id": 430, - "name": "NeoCook Iron 289A", - "description": "NeoCook Iron 289A is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.6, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2072, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 9.6, - "mass": 0.32, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 21.2, - "mass": 0.76, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 49.0, - "mass": 0.86, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 20.2, - "mass": 0.28, - "unit": "kg" - } - ], - "components": [ - { - "id": 4301, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 4302, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 4303, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 430, - "url": "https://via.placeholder.com/400x300/0be98b/ffffff?text=NeoCook+Iron+289A", - "description": "Iron product image" - } - ], - "created_at": "2024-11-30T09:16:11.475715Z", - "updated_at": "2025-06-24T09:16:11.475715Z" - }, - { - "id": 431, - "name": "PureLife Kettle 460L", - "description": "PureLife Kettle 460L is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.64, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 29.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1434, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 13.4, - "mass": 0.15, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 33.9, - "mass": 1.3, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 40.2, - "mass": 1.37, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 12.6, - "mass": 0.3, - "unit": "kg" - } - ], - "components": [ - { - "id": 4311, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4312, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 431, - "url": "https://via.placeholder.com/400x300/084271/ffffff?text=PureLife+Kettle+460L", - "description": "Kettle product image" - } - ], - "created_at": "2024-09-19T09:16:11.475778Z", - "updated_at": "2025-05-17T09:16:11.475778Z" - }, - { - "id": 432, - "name": "AquaPro Toaster 281P", - "description": "AquaPro Toaster 281P is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.73, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 870, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 34.2, - "mass": 0.55, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 28.9, - "mass": 0.66, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 36.8, - "mass": 0.6, - "unit": "kg" - } - ], - "components": [ - { - "id": 4321, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4322, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4323, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4324, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4325, - "name": "Toaster Component 5", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 432, - "url": "https://via.placeholder.com/400x300/077efd/ffffff?text=AquaPro+Toaster+281P", - "description": "Toaster product image" - } - ], - "created_at": "2024-07-12T09:16:11.475843Z", - "updated_at": "2025-05-11T09:16:11.475843Z" - }, - { - "id": 433, - "name": "SmartHome Monitor 901X", - "description": "SmartHome Monitor 901X is a high-performance monitor designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.0, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1353, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 18.8, - "mass": 0.4, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 18.8, - "mass": 0.68, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 50.5, - "mass": 1.12, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 11.9, - "mass": 0.21, - "unit": "kg" - } - ], - "components": [ - { - "id": 4331, - "name": "Monitor Component 1", - "description": "High-quality part for monitor functionality" - }, - { - "id": 4332, - "name": "Monitor Component 2", - "description": "High-quality part for monitor functionality" - }, - { - "id": 4333, - "name": "Monitor Component 3", - "description": "High-quality part for monitor functionality" - } - ], - "images": [ - { - "id": 433, - "url": "https://via.placeholder.com/400x300/087626/ffffff?text=SmartHome+Monitor+901X", - "description": "Monitor product image" - } - ], - "created_at": "2025-03-06T09:16:11.475897Z", - "updated_at": "2025-07-30T09:16:11.475897Z" - }, - { - "id": 434, - "name": "CleanWave Humidifier 779Q", - "description": "CleanWave Humidifier 779Q is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.85, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1038, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 22.6, - "mass": 0.3, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 28.4, - "mass": 0.35, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 24.5, - "mass": 0.77, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 24.5, - "mass": 0.25, - "unit": "kg" - } - ], - "components": [ - { - "id": 4341, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4342, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4343, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4344, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4345, - "name": "Humidifier Component 5", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 434, - "url": "https://via.placeholder.com/400x300/036e7d/ffffff?text=CleanWave+Humidifier+779Q", - "description": "Humidifier product image" - } - ], - "created_at": "2024-06-24T09:16:11.475966Z", - "updated_at": "2025-05-21T09:16:11.475966Z" - }, - { - "id": 435, - "name": "SmartHome Fan 532N", - "description": "SmartHome Fan 532N is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.69, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1683, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 47.6, - "mass": 0.67, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 22.2, - "mass": 0.48, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 30.2, - "mass": 0.45, - "unit": "kg" - } - ], - "components": [ - { - "id": 4351, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 4352, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 4353, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 4354, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 435, - "url": "https://via.placeholder.com/400x300/09cd55/ffffff?text=SmartHome+Fan+532N", - "description": "Fan product image" - } - ], - "created_at": "2025-04-23T09:16:11.476018Z", - "updated_at": "2025-05-18T09:16:11.476018Z" - }, - { - "id": 436, - "name": "CleanWave Toaster 710M", - "description": "CleanWave Toaster 710M is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.54, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1330, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 16.2, - "mass": 0.53, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 41.9, - "mass": 0.51, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 41.9, - "mass": 1.39, - "unit": "kg" - } - ], - "components": [ - { - "id": 4361, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4362, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4363, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 436, - "url": "https://via.placeholder.com/400x300/02d68b/ffffff?text=CleanWave+Toaster+710M", - "description": "Toaster product image" - } - ], - "created_at": "2024-11-16T09:16:11.476074Z", - "updated_at": "2025-05-08T09:16:11.476074Z" - }, - { - "id": 437, - "name": "PureLife Iron 355C", - "description": "PureLife Iron 355C is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.1, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2087, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 34.0, - "mass": 1.28, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 26.7, - "mass": 0.73, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 39.3, - "mass": 1.42, - "unit": "kg" - } - ], - "components": [ - { - "id": 4371, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 4372, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 437, - "url": "https://via.placeholder.com/400x300/08620d/ffffff?text=PureLife+Iron+355C", - "description": "Iron product image" - } - ], - "created_at": "2025-04-11T09:16:11.476135Z", - "updated_at": "2025-05-19T09:16:11.476135Z" - }, - { - "id": 438, - "name": "EcoTech Toaster 539U", - "description": "EcoTech Toaster 539U is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.5, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.2, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1358, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 52.8, - "mass": 1.03, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 47.2, - "mass": 1.8, - "unit": "kg" - } - ], - "components": [ - { - "id": 4381, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4382, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4383, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4384, - "name": "Toaster Component 4", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4385, - "name": "Toaster Component 5", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 438, - "url": "https://via.placeholder.com/400x300/0c9b2b/ffffff?text=EcoTech+Toaster+539U", - "description": "Toaster product image" - } - ], - "created_at": "2024-04-23T09:16:11.476261Z", - "updated_at": "2025-05-24T09:16:11.476261Z" - }, - { - "id": 439, - "name": "SmartHome Rice Cooker 285G", - "description": "SmartHome Rice Cooker 285G is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.53, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 21.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 16.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2181, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 17.7, - "mass": 0.46, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 32.9, - "mass": 0.98, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 32.3, - "mass": 0.36, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 17.1, - "mass": 0.28, - "unit": "kg" - } - ], - "components": [ - { - "id": 4391, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4392, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4393, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4394, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 439, - "url": "https://via.placeholder.com/400x300/07ebf1/ffffff?text=SmartHome+Rice+Cooker+285G", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-06-21T09:16:11.476341Z", - "updated_at": "2025-06-05T09:16:11.476341Z" - }, - { - "id": 440, - "name": "ZenGear Air Purifier 922X", - "description": "ZenGear Air Purifier 922X is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.93, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2132, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 9.6, - "mass": 0.31, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 31.8, - "mass": 0.79, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 31.8, - "mass": 0.92, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 26.8, - "mass": 0.42, - "unit": "kg" - } - ], - "components": [ - { - "id": 4401, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4402, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 440, - "url": "https://via.placeholder.com/400x300/03ed09/ffffff?text=ZenGear+Air+Purifier+922X", - "description": "Air Purifier product image" - } - ], - "created_at": "2025-03-28T09:16:11.476411Z", - "updated_at": "2025-06-19T09:16:11.476411Z" - }, - { - "id": 441, - "name": "PureLife Fan 359O", - "description": "PureLife Fan 359O is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.43, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1357, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 43.4, - "mass": 1.18, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 46.7, - "mass": 1.85, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 9.8, - "mass": 0.22, - "unit": "kg" - } - ], - "components": [ - { - "id": 4411, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 4412, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 4413, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 441, - "url": "https://via.placeholder.com/400x300/0bf962/ffffff?text=PureLife+Fan+359O", - "description": "Fan product image" - } - ], - "created_at": "2025-02-01T09:16:11.476468Z", - "updated_at": "2025-06-18T09:16:11.476468Z" - }, - { - "id": 442, - "name": "AquaPro Air Purifier 832G", - "description": "AquaPro Air Purifier 832G is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.07, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.9, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1964, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 33.8, - "mass": 0.39, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 66.2, - "mass": 0.86, - "unit": "kg" - } - ], - "components": [ - { - "id": 4421, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4422, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4423, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4424, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 442, - "url": "https://via.placeholder.com/400x300/08f9a1/ffffff?text=AquaPro+Air+Purifier+832G", - "description": "Air Purifier product image" - } - ], - "created_at": "2024-11-17T09:16:11.476511Z", - "updated_at": "2025-05-19T09:16:11.476511Z" - }, - { - "id": 443, - "name": "CleanWave Blender 185N", - "description": "CleanWave Blender 185N is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.99, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 27.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 24.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1667, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 54.5, - "mass": 1.31, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 45.5, - "mass": 1.4, - "unit": "kg" - } - ], - "components": [ - { - "id": 4431, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 4432, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 4433, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 4434, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - }, - { - "id": 4435, - "name": "Blender Component 5", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 443, - "url": "https://via.placeholder.com/400x300/073b00/ffffff?text=CleanWave+Blender+185N", - "description": "Blender product image" - } - ], - "created_at": "2024-10-18T09:16:11.476570Z", - "updated_at": "2025-05-28T09:16:11.476570Z" - }, - { - "id": 444, - "name": "PureLife Coffee Maker 966X", - "description": "PureLife Coffee Maker 966X is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.75, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1017, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 33.3, - "mass": 1.15, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 25.3, - "mass": 0.76, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 31.3, - "mass": 1.02, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 10.0, - "mass": 0.25, - "unit": "kg" - } - ], - "components": [ - { - "id": 4441, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4442, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4443, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4444, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4445, - "name": "Coffee Maker Component 5", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 444, - "url": "https://via.placeholder.com/400x300/06a2c2/ffffff?text=PureLife+Coffee+Maker+966X", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-04-16T09:16:11.476634Z", - "updated_at": "2025-07-16T09:16:11.476634Z" - }, - { - "id": 445, - "name": "AquaPro Kettle 745H", - "description": "AquaPro Kettle 745H is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.43, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 33.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1142, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 13.2, - "mass": 0.5, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 46.2, - "mass": 1.67, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 11.0, - "mass": 0.19, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 29.7, - "mass": 1.1, - "unit": "kg" - } - ], - "components": [ - { - "id": 4451, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4452, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 445, - "url": "https://via.placeholder.com/400x300/0ee055/ffffff?text=AquaPro+Kettle+745H", - "description": "Kettle product image" - } - ], - "created_at": "2024-06-15T09:16:11.476691Z", - "updated_at": "2025-07-29T09:16:11.476691Z" - }, - { - "id": 446, - "name": "ZenGear Fan 678V", - "description": "ZenGear Fan 678V is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.25, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.9, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1730, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.1, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 37.0, - "mass": 1.28, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 21.7, - "mass": 0.42, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 41.3, - "mass": 1.49, - "unit": "kg" - } - ], - "components": [ - { - "id": 4461, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 4462, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 4463, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 4464, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - }, - { - "id": 4465, - "name": "Fan Component 5", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 446, - "url": "https://via.placeholder.com/400x300/05d429/ffffff?text=ZenGear+Fan+678V", - "description": "Fan product image" - } - ], - "created_at": "2024-11-08T09:16:11.476753Z", - "updated_at": "2025-05-10T09:16:11.476753Z" - }, - { - "id": 447, - "name": "EcoTech Toaster 720C", - "description": "EcoTech Toaster 720C is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.97, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1057, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 30.9, - "mass": 0.52, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 27.2, - "mass": 0.4, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 23.6, - "mass": 0.38, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 18.3, - "mass": 0.26, - "unit": "kg" - } - ], - "components": [ - { - "id": 4471, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4472, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4473, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 447, - "url": "https://via.placeholder.com/400x300/0a3899/ffffff?text=EcoTech+Toaster+720C", - "description": "Toaster product image" - } - ], - "created_at": "2025-03-05T09:16:11.476805Z", - "updated_at": "2025-06-21T09:16:11.476805Z" - }, - { - "id": 448, - "name": "NeoCook Toaster 243Y", - "description": "NeoCook Toaster 243Y is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.62, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 15.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 824, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 39.2, - "mass": 0.71, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 20.0, - "mass": 0.59, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 40.8, - "mass": 1.27, - "unit": "kg" - } - ], - "components": [ - { - "id": 4481, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4482, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 448, - "url": "https://via.placeholder.com/400x300/0c37ce/ffffff?text=NeoCook+Toaster+243Y", - "description": "Toaster product image" - } - ], - "created_at": "2025-01-01T09:16:11.476854Z", - "updated_at": "2025-07-07T09:16:11.476854Z" - }, - { - "id": 449, - "name": "PureLife Blender 497A", - "description": "PureLife Blender 497A is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.51, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2199, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 69.0, - "mass": 2.25, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 31.0, - "mass": 0.45, - "unit": "kg" - } - ], - "components": [ - { - "id": 4491, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 4492, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 4493, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 449, - "url": "https://via.placeholder.com/400x300/0b6726/ffffff?text=PureLife+Blender+497A", - "description": "Blender product image" - } - ], - "created_at": "2025-04-17T09:16:11.476910Z", - "updated_at": "2025-06-14T09:16:11.476910Z" - }, - { - "id": 450, - "name": "EcoTech Kettle 421L", - "description": "EcoTech Kettle 421L is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.9, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 25.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2084, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 8.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 17.2, - "mass": 0.49, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 21.7, - "mass": 0.65, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 36.9, - "mass": 1.45, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 24.2, - "mass": 0.84, - "unit": "kg" - } - ], - "components": [ - { - "id": 4501, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4502, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 450, - "url": "https://via.placeholder.com/400x300/0b449b/ffffff?text=EcoTech+Kettle+421L", - "description": "Kettle product image" - } - ], - "created_at": "2024-04-19T09:16:11.476957Z", - "updated_at": "2025-07-03T09:16:11.476957Z" - }, - { - "id": 451, - "name": "CleanWave Blender 300G", - "description": "CleanWave Blender 300G is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.51, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.4, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1628, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 31.0, - "mass": 0.6, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 69.0, - "mass": 2.65, - "unit": "kg" - } - ], - "components": [ - { - "id": 4511, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 4512, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 4513, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - }, - { - "id": 4514, - "name": "Blender Component 4", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 451, - "url": "https://via.placeholder.com/400x300/01dd26/ffffff?text=CleanWave+Blender+300G", - "description": "Blender product image" - } - ], - "created_at": "2024-08-22T09:16:11.477006Z", - "updated_at": "2025-06-23T09:16:11.477006Z" - }, - { - "id": 452, - "name": "EcoTech Coffee Maker 155V", - "description": "EcoTech Coffee Maker 155V is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.3, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 28.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1841, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.7, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 11.7, - "mass": 0.4, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 31.1, - "mass": 0.5, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 57.3, - "mass": 2.26, - "unit": "kg" - } - ], - "components": [ - { - "id": 4521, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4522, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4523, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4524, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 452, - "url": "https://via.placeholder.com/400x300/061a83/ffffff?text=EcoTech+Coffee+Maker+155V", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-08-10T09:16:11.477058Z", - "updated_at": "2025-05-19T09:16:11.477058Z" - }, - { - "id": 453, - "name": "NeoCook Rice Cooker 568E", - "description": "NeoCook Rice Cooker 568E is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.54, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1288, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.1, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.7, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 27.1, - "mass": 0.71, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 25.2, - "mass": 0.95, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 27.5, - "mass": 0.86, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 20.2, - "mass": 0.73, - "unit": "kg" - } - ], - "components": [ - { - "id": 4531, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4532, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4533, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4534, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4535, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 453, - "url": "https://via.placeholder.com/400x300/0f2752/ffffff?text=NeoCook+Rice+Cooker+568E", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-04-12T09:16:11.477115Z", - "updated_at": "2025-04-30T09:16:11.477115Z" - }, - { - "id": 454, - "name": "ZenGear Humidifier 186G", - "description": "ZenGear Humidifier 186G is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.42, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.1, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1727, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 69.5, - "mass": 1.7, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 30.5, - "mass": 0.77, - "unit": "kg" - } - ], - "components": [ - { - "id": 4541, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4542, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4543, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4544, - "name": "Humidifier Component 4", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 454, - "url": "https://via.placeholder.com/400x300/07f064/ffffff?text=ZenGear+Humidifier+186G", - "description": "Humidifier product image" - } - ], - "created_at": "2025-04-05T09:16:11.477159Z", - "updated_at": "2025-05-20T09:16:11.477159Z" - }, - { - "id": 455, - "name": "EcoTech Iron 315T", - "description": "EcoTech Iron 315T is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.94, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1804, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 26.0, - "mass": 0.32, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 34.7, - "mass": 0.9, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 39.3, - "mass": 1.02, - "unit": "kg" - } - ], - "components": [ - { - "id": 4551, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 4552, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 4553, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 455, - "url": "https://via.placeholder.com/400x300/0e01e7/ffffff?text=EcoTech+Iron+315T", - "description": "Iron product image" - } - ], - "created_at": "2025-02-10T09:16:11.477219Z", - "updated_at": "2025-06-16T09:16:11.477219Z" - }, - { - "id": 456, - "name": "PureLife Rice Cooker 170F", - "description": "PureLife Rice Cooker 170F is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.05, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 2053, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.9, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 33.8, - "mass": 0.57, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 16.2, - "mass": 0.19, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 50.0, - "mass": 1.01, - "unit": "kg" - } - ], - "components": [ - { - "id": 4561, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4562, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4563, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4564, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4565, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 456, - "url": "https://via.placeholder.com/400x300/0f326d/ffffff?text=PureLife+Rice+Cooker+170F", - "description": "Rice Cooker product image" - } - ], - "created_at": "2025-03-09T09:16:11.477266Z", - "updated_at": "2025-05-13T09:16:11.477266Z" - }, - { - "id": 457, - "name": "ZenGear Blender 780C", - "description": "ZenGear Blender 780C is a high-performance blender designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.14, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1230, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 71.2, - "mass": 1.89, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 28.8, - "mass": 0.33, - "unit": "kg" - } - ], - "components": [ - { - "id": 4571, - "name": "Blender Component 1", - "description": "High-quality part for blender functionality" - }, - { - "id": 4572, - "name": "Blender Component 2", - "description": "High-quality part for blender functionality" - }, - { - "id": 4573, - "name": "Blender Component 3", - "description": "High-quality part for blender functionality" - } - ], - "images": [ - { - "id": 457, - "url": "https://via.placeholder.com/400x300/070d39/ffffff?text=ZenGear+Blender+780C", - "description": "Blender product image" - } - ], - "created_at": "2024-09-13T09:16:11.477320Z", - "updated_at": "2025-04-29T09:16:11.477320Z" - }, - { - "id": 458, - "name": "EcoTech Kettle 891Z", - "description": "EcoTech Kettle 891Z is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.46, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1452, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.3, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 10.8, - "mass": 0.23, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 14.4, - "mass": 0.19, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 32.4, - "mass": 0.37, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 42.3, - "mass": 0.99, - "unit": "kg" - } - ], - "components": [ - { - "id": 4581, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4582, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 458, - "url": "https://via.placeholder.com/400x300/029982/ffffff?text=EcoTech+Kettle+891Z", - "description": "Kettle product image" - } - ], - "created_at": "2025-03-13T09:16:11.477371Z", - "updated_at": "2025-07-20T09:16:11.477371Z" - }, - { - "id": 459, - "name": "CleanWave Coffee Maker 655L", - "description": "CleanWave Coffee Maker 655L is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.85, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 20.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1232, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.2, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 26.4, - "mass": 0.74, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 50.9, - "mass": 1.5, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 22.6, - "mass": 0.44, - "unit": "kg" - } - ], - "components": [ - { - "id": 4591, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4592, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 459, - "url": "https://via.placeholder.com/400x300/05470f/ffffff?text=CleanWave+Coffee+Maker+655L", - "description": "Coffee Maker product image" - } - ], - "created_at": "2025-03-18T09:16:11.477427Z", - "updated_at": "2025-05-30T09:16:11.477427Z" - }, - { - "id": 460, - "name": "NeoCook Iron 823D", - "description": "NeoCook Iron 823D is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 5, - "name": "Office Equipment" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.98, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.3, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1364, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 40.8, - "mass": 0.76, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 41.5, - "mass": 1.54, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 7.7, - "mass": 0.19, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 9.9, - "mass": 0.3, - "unit": "kg" - } - ], - "components": [ - { - "id": 4601, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 4602, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 4603, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - }, - { - "id": 4604, - "name": "Iron Component 4", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 460, - "url": "https://via.placeholder.com/400x300/0b962a/ffffff?text=NeoCook+Iron+823D", - "description": "Iron product image" - } - ], - "created_at": "2024-09-19T09:16:11.477485Z", - "updated_at": "2025-07-04T09:16:11.477485Z" - }, - { - "id": 461, - "name": "SmartHome Air Purifier 791E", - "description": "SmartHome Air Purifier 791E is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.4, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 23.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 17.8, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1403, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.5, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 23.3, - "mass": 0.88, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 76.7, - "mass": 1.54, - "unit": "kg" - } - ], - "components": [ - { - "id": 4611, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4612, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4613, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 461, - "url": "https://via.placeholder.com/400x300/03ea2a/ffffff?text=SmartHome+Air+Purifier+791E", - "description": "Air Purifier product image" - } - ], - "created_at": "2025-01-14T09:16:11.477537Z", - "updated_at": "2025-07-26T09:16:11.477537Z" - }, - { - "id": 462, - "name": "AquaPro Fan 807U", - "description": "AquaPro Fan 807U is a high-performance fan designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.57, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 23.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1872, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.0, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 28.1, - "mass": 1.11, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 25.0, - "mass": 0.75, - "unit": "kg" - }, - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 31.2, - "mass": 1.24, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 15.6, - "mass": 0.45, - "unit": "kg" - } - ], - "components": [ - { - "id": 4621, - "name": "Fan Component 1", - "description": "High-quality part for fan functionality" - }, - { - "id": 4622, - "name": "Fan Component 2", - "description": "High-quality part for fan functionality" - }, - { - "id": 4623, - "name": "Fan Component 3", - "description": "High-quality part for fan functionality" - }, - { - "id": 4624, - "name": "Fan Component 4", - "description": "High-quality part for fan functionality" - }, - { - "id": 4625, - "name": "Fan Component 5", - "description": "High-quality part for fan functionality" - } - ], - "images": [ - { - "id": 462, - "url": "https://via.placeholder.com/400x300/063653/ffffff?text=AquaPro+Fan+807U", - "description": "Fan product image" - } - ], - "created_at": "2024-12-13T09:16:11.477595Z", - "updated_at": "2025-04-27T09:16:11.477595Z" - }, - { - "id": 463, - "name": "ChefMate Air Purifier 355V", - "description": "ChefMate Air Purifier 355V is a high-performance air purifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.02, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 22.6, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.3, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 915, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 6.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 41.2, - "mass": 1.03, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 33.8, - "mass": 0.7, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 25.0, - "mass": 0.65, - "unit": "kg" - } - ], - "components": [ - { - "id": 4631, - "name": "Air Purifier Component 1", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4632, - "name": "Air Purifier Component 2", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4633, - "name": "Air Purifier Component 3", - "description": "High-quality part for air purifier functionality" - }, - { - "id": 4634, - "name": "Air Purifier Component 4", - "description": "High-quality part for air purifier functionality" - } - ], - "images": [ - { - "id": 463, - "url": "https://via.placeholder.com/400x300/05b042/ffffff?text=ChefMate+Air+Purifier+355V", - "description": "Air Purifier product image" - } - ], - "created_at": "2025-02-27T09:16:11.477641Z", - "updated_at": "2025-05-30T09:16:11.477641Z" - }, - { - "id": 464, - "name": "ChefMate Iron 529H", - "description": "ChefMate Iron 529H is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 1, - "name": "Kitchen Appliance" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.67, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 29.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 27.1, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1102, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.8, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.4, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.2, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 53.1, - "mass": 1.48, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 46.9, - "mass": 1.68, - "unit": "kg" - } - ], - "components": [ - { - "id": 4641, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 4642, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - }, - { - "id": 4643, - "name": "Iron Component 3", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 464, - "url": "https://via.placeholder.com/400x300/02967a/ffffff?text=ChefMate+Iron+529H", - "description": "Iron product image" - } - ], - "created_at": "2024-07-21T09:16:11.477693Z", - "updated_at": "2025-07-07T09:16:11.477693Z" - }, - { - "id": 465, - "name": "CleanWave Rice Cooker 115N", - "description": "CleanWave Rice Cooker 115N is a high-performance rice cooker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.64, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.8, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 26.7, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1949, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.3, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.6, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.9, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 75.0, - "mass": 2.68, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 25.0, - "mass": 0.91, - "unit": "kg" - } - ], - "components": [ - { - "id": 4651, - "name": "Rice Cooker Component 1", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4652, - "name": "Rice Cooker Component 2", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4653, - "name": "Rice Cooker Component 3", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4654, - "name": "Rice Cooker Component 4", - "description": "High-quality part for rice cooker functionality" - }, - { - "id": 4655, - "name": "Rice Cooker Component 5", - "description": "High-quality part for rice cooker functionality" - } - ], - "images": [ - { - "id": 465, - "url": "https://via.placeholder.com/400x300/0d635d/ffffff?text=CleanWave+Rice+Cooker+115N", - "description": "Rice Cooker product image" - } - ], - "created_at": "2024-05-12T09:16:11.477737Z", - "updated_at": "2025-07-09T09:16:11.477737Z" - }, - { - "id": 466, - "name": "PureLife Coffee Maker 908L", - "description": "PureLife Coffee Maker 908L is a high-performance coffee maker designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.18, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 25.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 20.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1209, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.4, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 33.8, - "mass": 0.82, - "unit": "kg" - }, - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 66.2, - "mass": 1.98, - "unit": "kg" - } - ], - "components": [ - { - "id": 4661, - "name": "Coffee Maker Component 1", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4662, - "name": "Coffee Maker Component 2", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4663, - "name": "Coffee Maker Component 3", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4664, - "name": "Coffee Maker Component 4", - "description": "High-quality part for coffee maker functionality" - }, - { - "id": 4665, - "name": "Coffee Maker Component 5", - "description": "High-quality part for coffee maker functionality" - } - ], - "images": [ - { - "id": 466, - "url": "https://via.placeholder.com/400x300/051590/ffffff?text=PureLife+Coffee+Maker+908L", - "description": "Coffee Maker product image" - } - ], - "created_at": "2024-03-21T09:16:11.477795Z", - "updated_at": "2025-07-02T09:16:11.477795Z" - }, - { - "id": 467, - "name": "CleanWave Humidifier 336J", - "description": "CleanWave Humidifier 336J is a high-performance humidifier designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 2, - "name": "Home Electronics" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 1.65, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 34.7, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 28.6, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 833, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 7.6, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 5.9, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 8.6, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 25.5, - "mass": 0.79, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 38.2, - "mass": 0.93, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 36.4, - "mass": 0.81, - "unit": "kg" - } - ], - "components": [ - { - "id": 4671, - "name": "Humidifier Component 1", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4672, - "name": "Humidifier Component 2", - "description": "High-quality part for humidifier functionality" - }, - { - "id": 4673, - "name": "Humidifier Component 3", - "description": "High-quality part for humidifier functionality" - } - ], - "images": [ - { - "id": 467, - "url": "https://via.placeholder.com/400x300/02e324/ffffff?text=CleanWave+Humidifier+336J", - "description": "Humidifier product image" - } - ], - "created_at": "2024-10-29T09:16:11.477849Z", - "updated_at": "2025-07-14T09:16:11.477849Z" - }, - { - "id": 468, - "name": "ZenGear Kettle 160L", - "description": "ZenGear Kettle 160L is a high-performance kettle designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 3, - "name": "Personal Care Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 3.26, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 26.2, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 21.4, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1922, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 9.5, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 8.3, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 6.1, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 1, - "name": "Stainless Steel", - "type": "Metal" - }, - "percentage": 20.4, - "mass": 0.56, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 20.9, - "mass": 0.61, - "unit": "kg" - }, - { - "material": { - "id": 3, - "name": "Copper Wire", - "type": "Metal" - }, - "percentage": 30.6, - "mass": 0.95, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 28.1, - "mass": 0.99, - "unit": "kg" - } - ], - "components": [ - { - "id": 4681, - "name": "Kettle Component 1", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4682, - "name": "Kettle Component 2", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4683, - "name": "Kettle Component 3", - "description": "High-quality part for kettle functionality" - }, - { - "id": 4684, - "name": "Kettle Component 4", - "description": "High-quality part for kettle functionality" - } - ], - "images": [ - { - "id": 468, - "url": "https://via.placeholder.com/400x300/0be076/ffffff?text=ZenGear+Kettle+160L", - "description": "Kettle product image" - } - ], - "created_at": "2024-09-09T09:16:11.477905Z", - "updated_at": "2025-05-21T09:16:11.477905Z" - }, - { - "id": 469, - "name": "CleanWave Iron 879A", - "description": "CleanWave Iron 879A is a high-performance iron designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.39, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 31.0, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 22.0, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1804, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.2, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 7.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 9.5, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 16.0, - "mass": 0.27, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 31.2, - "mass": 0.86, - "unit": "kg" - }, - { - "material": { - "id": 5, - "name": "Borosilicate Glass", - "type": "Glass" - }, - "percentage": 12.5, - "mass": 0.29, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 40.3, - "mass": 0.64, - "unit": "kg" - } - ], - "components": [ - { - "id": 4691, - "name": "Iron Component 1", - "description": "High-quality part for iron functionality" - }, - { - "id": 4692, - "name": "Iron Component 2", - "description": "High-quality part for iron functionality" - } - ], - "images": [ - { - "id": 469, - "url": "https://via.placeholder.com/400x300/0695b3/ffffff?text=CleanWave+Iron+879A", - "description": "Iron product image" - } - ], - "created_at": "2025-02-03T09:16:11.477957Z", - "updated_at": "2025-07-07T09:16:11.477957Z" - }, - { - "id": 470, - "name": "EcoTech Toaster 210H", - "description": "EcoTech Toaster 210H is a high-performance toaster designed for modern households, featuring advanced controls and energy-efficient components.", - "product_type": { - "id": 4, - "name": "Smart Home Device" - }, - "physical_properties": [ - { - "property_name": "Weight", - "value": 2.84, - "unit": "kg" - }, - { - "property_name": "Height", - "value": 32.5, - "unit": "cm" - }, - { - "property_name": "Width", - "value": 18.5, - "unit": "cm" - }, - { - "property_name": "Power", - "value": 1042, - "unit": "W" - } - ], - "circularity_properties": [ - { - "property_name": "Recyclability Score", - "value": 6.7, - "unit": "/10" - }, - { - "property_name": "Repairability Score", - "value": 9.0, - "unit": "/10" - }, - { - "property_name": "Energy Efficiency", - "value": 7.8, - "unit": "/10" - } - ], - "bill_of_materials": [ - { - "material": { - "id": 7, - "name": "Silicone", - "type": "Elastomer" - }, - "percentage": 34.7, - "mass": 1.34, - "unit": "kg" - }, - { - "material": { - "id": 6, - "name": "Polypropylene", - "type": "Plastic" - }, - "percentage": 27.1, - "mass": 0.89, - "unit": "kg" - }, - { - "material": { - "id": 4, - "name": "Mica", - "type": "Mineral" - }, - "percentage": 24.7, - "mass": 0.4, - "unit": "kg" - }, - { - "material": { - "id": 2, - "name": "ABS Plastic", - "type": "Plastic" - }, - "percentage": 13.5, - "mass": 0.41, - "unit": "kg" - } - ], - "components": [ - { - "id": 4701, - "name": "Toaster Component 1", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4702, - "name": "Toaster Component 2", - "description": "High-quality part for toaster functionality" - }, - { - "id": 4703, - "name": "Toaster Component 3", - "description": "High-quality part for toaster functionality" - } - ], - "images": [ - { - "id": 470, - "url": "https://via.placeholder.com/400x300/01f0e0/ffffff?text=EcoTech+Toaster+210H", - "description": "Toaster product image" - } - ], - "created_at": "2025-04-08T09:16:11.478016Z", - "updated_at": "2025-04-28T09:16:11.478016Z" - }, - ] -} diff --git a/frontend-app/src/assets/data/products.json b/frontend-app/src/assets/data/products.json deleted file mode 100644 index 9fcc26d..0000000 --- a/frontend-app/src/assets/data/products.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "1": - { - "created_at": "2025-06-30T11:02:56.661198Z", - "updated_at": "2025-06-30T11:02:56.661198Z", - "name": "iPhone 12", - "description": "Apple smartphone.", - "brand": "Apple", - "model": "A2403", - "id": 1, - "physical_properties": { - "created_at": "2025-06-30T11:02:56Z", - "updated_at": "2025-06-30T11:02:56Z", - "weight_kg": 0.164, - "height_cm": 14.7, - "width_cm": 7.15, - "depth_cm": 0.74, - "id": 1, - "volume_cm3": 77.777 - }, - "product_type": { - "id": 1, - "name": "32250000-0", - "description": "Mobile telephones" - }, - "images": [{ - "id": 1, - "image_url": "https://reebelo.com/_next/image?url=https%3A%2F%2Fcdn.reebelo.com%2Fpim%2Fproducts%2FP-IPHONE12MINI%2FBLA-image-0.jpg&w=256&q=75", - "description": "iPhone 12 Image" - }], - "videos": [], - "files": [], - "bill_of_materials": [], - "components": [] - }, - "2": - { - "created_at": "2025-06-30T11:02:56.693608Z", - "updated_at": "2025-06-30T11:02:56.693608Z", - "name": "Dell XPS 13", - "description": "Dell laptop.", - "brand": "Dell", - "model": "XPS9380", - "dismantling_notes": null, - "dismantling_time_start": "2025-06-30T11:02:56Z", - "dismantling_time_end": null, - "id": 2, - "physical_properties": { - "created_at": "2025-06-30T11:02:56Z", - "updated_at": "2025-06-30T11:02:56Z", - "weight_kg": 0.164, - "height_cm": 14.7, - "width_cm": 7.15, - "depth_cm": 0.74, - "id": 1, - "volume_cm3": 77.777 - }, - "product_type": { - "id": 1, - "name": "32250000-0", - "description": "Mobile telephones" - }, - "images": [], - "videos": [], - "files": [], - "bill_of_materials": [], - "components": [ - { - "id": 3, - "name": "Screen Assembly", - "description": "LED 4k Touch screen assembly" - } - ] - }, - "3": - { - "created_at": "2025-06-30T11:02:56.693608Z", - "updated_at": "2025-06-30T11:02:56.693608Z", - "name": "Screen Assembly", - "description": "LED 4k Touch screen assembly", - "brand": "Dell", - "model": "bb-asd4-123234", - "dismantling_notes": null, - "dismantling_time_start": "2025-06-30T11:02:56Z", - "dismantling_time_end": null, - "id": 3, - "product_type_id": 2, - "physical_properties": { - "created_at": "2025-06-30T11:02:56Z", - "updated_at": "2025-06-30T11:02:56Z", - "weight_kg": 0.164, - "height_cm": 14.7, - "width_cm": 7.15, - "depth_cm": 0.74, - "id": 1, - "volume_cm3": 77.777 - }, - "product_type": { - "id": 1, - "name": "32250000-0", - "description": "Mobile telephones" - }, - "images": [], - "videos": [], - "files": [], - "bill_of_materials": [], - "components": [] - } -} From 793b2a5e76037d4ac492976e596efb298ac02b51 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 21 Oct 2025 13:47:37 +0200 Subject: [PATCH 04/79] fix(backend): Don't seed dummy data by default --- backend/scripts/seed/migrations_entrypoint.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/scripts/seed/migrations_entrypoint.sh b/backend/scripts/seed/migrations_entrypoint.sh index e48e483..9cc678c 100755 --- a/backend/scripts/seed/migrations_entrypoint.sh +++ b/backend/scripts/seed/migrations_entrypoint.sh @@ -23,6 +23,7 @@ fi if [ "$SEED_PRODUCT_TYPES" = "true" ]; then echo "Seeding product types..." .venv/bin/python -m scripts.seed.taxonomies.cpv --seed-product-types + fi # Check if all tables are empty @@ -31,7 +32,7 @@ echo "Checking if all tables in the database are empty using scripts/db_is_empty # Run the script and temporarily disable exit-on-error to capture the exit code DB_EMPTY=$(.venv/bin/python -m scripts.db_is_empty) -if [ "$DB_EMPTY" = "TRUE" ]; then +if [ "$DB_EMPTY" = "TRUE" ] && [ "$SEED_DUMMY_DATA" = "true" ]; then echo "All tables are empty, proceeding to seed dummy data..." .venv/bin/python -m scripts.seed.dummy_data else From eaf6b42a85ed93fb9ed3e9545dc8fd8afe1b0f97 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 21 Oct 2025 13:49:14 +0200 Subject: [PATCH 05/79] fix(backend): improve seeding scripts --- backend/scripts/seed/migrations_entrypoint.sh | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/backend/scripts/seed/migrations_entrypoint.sh b/backend/scripts/seed/migrations_entrypoint.sh index 9cc678c..e4fd77b 100755 --- a/backend/scripts/seed/migrations_entrypoint.sh +++ b/backend/scripts/seed/migrations_entrypoint.sh @@ -3,8 +3,17 @@ # Exit immediately if a command exits with a non-zero status set -e +# Helper to lowercase a value (POSIX) +lc() { echo "$1" | tr '[:upper:]' '[:lower:]'; } + +# Defaults (so missing env vars behave as "false") +SEED_TAXONOMIES="${SEED_TAXONOMIES:-false}" +SEED_PRODUCT_TYPES="${SEED_PRODUCT_TYPES:-false}" +SEED_DUMMY_DATA="${SEED_DUMMY_DATA:-false}" +DEBUG="${DEBUG:-false}" + # Run Alembic migrations -if [ "$DEBUG" = "True" ]; then +if [ "$(lc "$DEBUG")" = "true" ]; then echo "Current migration status:" .venv/bin/alembic current fi @@ -12,31 +21,26 @@ fi echo "Upgrading database to the latest revision..." .venv/bin/alembic upgrade head -# Check if we should seed taxonomies -if [ "$SEED_TAXONOMIES" = "true" ]; then +# Seed taxonomies — run cpv once and pass the product-types flag if requested +if [ "$(lc "$SEED_TAXONOMIES")" = "true" ]; then echo "Seeding taxonomies..." - .venv/bin/python -m scripts.seed.taxonomies.cpv + if [ "$(lc "$SEED_PRODUCT_TYPES")" = "true" ]; then + .venv/bin/python -m scripts.seed.taxonomies.cpv --seed-product-types + else + .venv/bin/python -m scripts.seed.taxonomies.cpv + fi .venv/bin/python -m scripts.seed.taxonomies.harmonized_system fi -# Check if we should seed product types -if [ "$SEED_PRODUCT_TYPES" = "true" ]; then - echo "Seeding product types..." - .venv/bin/python -m scripts.seed.taxonomies.cpv --seed-product-types - -fi - # Check if all tables are empty echo "Checking if all tables in the database are empty using scripts/db_is_empty.py..." - -# Run the script and temporarily disable exit-on-error to capture the exit code DB_EMPTY=$(.venv/bin/python -m scripts.db_is_empty) -if [ "$DB_EMPTY" = "TRUE" ] && [ "$SEED_DUMMY_DATA" = "true" ]; then +if [ "$(lc "$DB_EMPTY")" = "true" ] && [ "$(lc "$SEED_DUMMY_DATA")" = "true" ]; then echo "All tables are empty, proceeding to seed dummy data..." .venv/bin/python -m scripts.seed.dummy_data else - echo "Database already has data, skipping seeding." + echo "Database already has data or dummy data seeding disabled, skipping." fi # Create a superuser if the required environment variables are set @@ -45,3 +49,4 @@ echo "Creating a superuser..." # Start the server or other desired commands exec "$@" +# ...existing code... \ No newline at end of file From 6a59854f5dd2d0a5fd7a188f647a99811bce12be Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 28 Oct 2025 11:01:52 +0000 Subject: [PATCH 06/79] fix(docker): Fix postgres-backup-local config --- compose.prod.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compose.prod.yml b/compose.prod.yml index 6e20e2c..656a48f 100644 --- a/compose.prod.yml +++ b/compose.prod.yml @@ -46,7 +46,8 @@ services: environment: SCHEDULE: 0 2 * * * # Daily at 2am POSTGRES_HOST: database - POSTGRES_EXTRA_OPTS: "-Z1 --schema: public --blobs" # Compress backups, only back up public schema, include blobs + BACKUP_ON_STARTUP: true + POSTGRES_EXTRA_OPTS: "--compress=zstd:3 --schema=public" # Compress backups, only back up public schema BACKUP_KEEP_DAYS: 7 BACKUP_KEEP_WEEKS: 4 BACKUP_KEEP_MONTHS: 6 From 8774ecfd26a2468d121ce2028f2d89e7fe499ffd Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 28 Oct 2025 12:12:05 +0100 Subject: [PATCH 07/79] feat(backend): add manual pg backup script and rclone backup sync script --- .env.example | 10 +- backend/scripts/backup/README.md | 136 +++++++++++++++++++ backend/scripts/backup/backup_pg_database.sh | 63 +++++++++ backend/scripts/backup/rclone_backup.sh | 42 ++++++ backend/scripts/backup/rsync_backup.sh | 8 +- 5 files changed, 252 insertions(+), 7 deletions(-) create mode 100644 backend/scripts/backup/README.md create mode 100755 backend/scripts/backup/backup_pg_database.sh create mode 100755 backend/scripts/backup/rclone_backup.sh diff --git a/.env.example b/.env.example index b371bd6..00aa2f7 100644 --- a/.env.example +++ b/.env.example @@ -9,6 +9,10 @@ TUNNEL_TOKEN=your_token # Host directory where database and user upload backups are stored BACKUP_DIR=./backups -# Remote backup config (for use of backend/scripts/backup/backup_rclone.sh script) -BACKUP_REMOTE_HOST=user@host -BACKUP_REMOTE_PATH=/path/to/remote/backup +# Remote rsync backup config (for use of backend/scripts/backup/rsync_backup.sh script) +BACKUP_RSYNC_REMOTE_HOST=user@host +BACKUP_RSYNC_REMOTE_PATH=/path/to/remote/backup + +# Remote rclone backup config (for use of backend/scripts/backup/rclone_backup.sh script) +BACKUP_RCLONE_REMOTE=myremote:/path/to/remote/backup +BACKUP_RCLONE_MULTI_THREAD_STREAMS=16 \ No newline at end of file diff --git a/backend/scripts/backup/README.md b/backend/scripts/backup/README.md new file mode 100644 index 0000000..e180501 --- /dev/null +++ b/backend/scripts/backup/README.md @@ -0,0 +1,136 @@ +# ReLab Data Backups + +Scripts for backing up ReLab data locally and syncing to remote storage. + +## Overview + +Two types of data are backed up: + +- **PostgreSQL database**: Compressed SQL dumps +- **User uploads**: Compressed tarballs of product images and files + +Backups are created locally first, then optionally synced to remote storage. + +--- + +## Local Backups + +### Configuration + +In the root [`.env`](../../../.env) file, set where backups are stored: + +```env +BACKUP_DIR=/path/to/local/backups +``` + +Ensure the directory exists and is writable by the Docker user. + +The backup scripts create subdirectories: + +- `$BACKUP_DIR/postgres_db`: PostgreSQL backups +- `$BACKUP_DIR/user_upload_backups`: User upload backups + +### Usage + +**Manual backup:** + +Run the backup scripts directly: + +```bash +./backup_user_uploads.sh +``` + +```bash +./backup_postgres_database.sh +``` + +> 💡 **Note:** By default these scripts back up services running on the host, not processes inside Docker containers. To back up Dockerized services, configure the scripts to back up the docker volume (for user uploads) or connect to the database container (for database backups). + +**Automated backup:** + +From the repo root, start the stack with the `backups` profile: + +```bash +docker compose -f compose.yml -f compose.prod.yml --profile backups up -d +``` + +This runs: + +- `backend_user_upload_backups`: Scheduled user upload backups, backed up to `$BACKUP_DIR/user_upload_backups` directory +- `database_backups`: Scheduled PostgreSQL backups, backed up to `$BACKUP_DIR/postgres_db` directory + +Backup schedules and retention policies are configured in [`compose.prod.yml`](../../../compose.prod.yml). + +--- + +## Remote Backups + +Optionally, you can sync local backups to remote storage using **rsync** (SSH/local network) or **rclone** (cloud/SFTP). Both scripts include safety checks to prevent data loss if the local directory is unexpectedly empty. + +### Option 1: rsync (SSH/Local Network) + +Ideal for fast local networks or SSH-accessible servers + +#### Prerequisites + +- SSH key-based authentication configured for the remote host +- `rsync` installed on both local and remote machines + +#### Configuration + +Add to root [`.env`](../../../.env) file: + +```env +BACKUP_RSYNC_REMOTE_HOST=user@hostname +BACKUP_RSYNC_REMOTE_PATH=/path/to/remote/backup +``` + +#### Usage + +**Manual sync:** + +```bash +./backend/scripts/backup/rsync_backup.sh +``` + +**Automated sync (cron):** + +```cron +# Daily at 3:30 AM +30 3 * * * /path/to/relab/backend/scripts/backup/rsync_backup.sh >> /var/log/relab/rsync_backup.log 2>&1 +``` + +--- + +### Option 2: rclone (Cloud/SFTP) + +Ideal for cloud storage (S3, SharePoint, Google Drive, etc.) + +#### Prerequisites + +- `rclone` installed +- Rclone remote configured with `rclone config` (SFTP, S3, SharePoint, etc.) + +#### Configuration + +Add to root [`.env`](../../../.env) file: + +```env +BACKUP_RCLONE_REMOTE=myremote:/backup/relab +BACKUP_RCLONE_MULTI_THREAD_STREAMS=16 # Optional: adjust for network speed +``` + +#### Usage + +**Manual sync:** + +```bash +./backend/scripts/backup/rclone_backup.sh +``` + +**Automated sync (cron):** + +```cron +# Daily at 3:30 AM +30 3 * * * /path/to/relab/backend/scripts/backup/rclone_backup.sh >> /var/log/relab/rclone_backup.log 2>&1 +``` diff --git a/backend/scripts/backup/backup_pg_database.sh b/backend/scripts/backup/backup_pg_database.sh new file mode 100755 index 0000000..c67e6f4 --- /dev/null +++ b/backend/scripts/backup/backup_pg_database.sh @@ -0,0 +1,63 @@ +#!/bin/sh +### Simple script to backup the postgres database manually +set -e + +# Load backend and root .env files +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PG_ENV_FILE="$SCRIPT_DIR/../../.env" +ROOT_ENV_FILE="$SCRIPT_DIR/../../../.env" + +if [ -f "$PG_ENV_FILE" ]; then + . "$PG_ENV_FILE" + echo "[$(date)] Loaded backend env file: $PG_ENV_FILE" +else + echo "[$(date)] ERROR: Backend env file not found at $PG_ENV_FILE. Aborting." + exit 1 +fi + +if [ -f "$ROOT_ENV_FILE" ]; then + . "$ROOT_ENV_FILE" + echo "[$(date)] Loaded root env file: $ROOT_ENV_FILE" +else + echo "[$(date)] INFO: Root env file not found at $ROOT_ENV_FILE. Skipping." +fi + +# Configuration +BACKUP_DIR_PG="${BACKUP_DIR:-$SCRIPT_DIR/../../backups}/postgres_db/manual" +DATABASE_HOST="${DATABASE_HOST:-localhost}" +DATABASE_PORT="${DATABASE_PORT:-5432}" +POSTGRES_USER="${POSTGRES_USER:-postgres}" +POSTGRES_PASSWORD="${POSTGRES_PASSWORD:?POSTGRES_PASSWORD not set}" +POSTGRES_DB="${POSTGRES_DB:?POSTGRES_DB not set}" + +COMPRESSION="${POSTGRES_COMPRESSION:-zstd:3}" +SCHEMA="${POSTGRES_SCHEMA:-public}" +FILENAME="${POSTGRES_DB}-$(date +%Y%m%d-%H%M%S).sql.zst" + +# Wait for PostgreSQL +echo "[$(date)] Waiting for PostgreSQL..." +for i in $(seq 1 10); do + if PGPASSWORD="$POSTGRES_PASSWORD" pg_isready -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$POSTGRES_USER" -q; then + echo "[$(date)] PostgreSQL ready" + break + fi + [ "$i" -eq 10 ] && { echo "[$(date)] ERROR: PostgreSQL timeout"; exit 1; } + sleep 2 +done + +echo "Successfully connected to PostgreSQL." + +# Perform backup +mkdir -p "$BACKUP_DIR_PG" +echo "[$(date)] Backing up '$POSTGRES_DB' to $BACKUP_DIR_PG/$FILENAME" + +PGPASSWORD="$POSTGRES_PASSWORD" pg_dump \ + -h "$DATABASE_HOST" \ + -p "$DATABASE_PORT" \ + -U "$POSTGRES_USER" \ + --compress="$COMPRESSION" \ + --schema="$SCHEMA" \ + "$POSTGRES_DB" \ + > "$BACKUP_DIR_PG/$FILENAME" + +echo "[$(date)] Backup completed. Size: $(du -h "$BACKUP_DIR_PG/$FILENAME" | cut -f1)" diff --git a/backend/scripts/backup/rclone_backup.sh b/backend/scripts/backup/rclone_backup.sh new file mode 100755 index 0000000..da25000 --- /dev/null +++ b/backend/scripts/backup/rclone_backup.sh @@ -0,0 +1,42 @@ +#!/bin/sh +### Rclone script to mirror a local backup directory to a remote server. +set -e + +# Load root .env file +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ENV_FILE="$SCRIPT_DIR/../../../.env" + +if [ -f "$ENV_FILE" ]; then + . "$ENV_FILE" + echo "[$(date)] Loaded env file: $ENV_FILE" +else + echo "[$(date)] ERROR: Env file not found at $ENV_FILE. Aborting." + exit 1 +fi + +# Configuration +BACKUP_DIR="${BACKUP_DIR:-$REPO_ROOT/backend/backups}" +BACKUP_RCLONE_REMOTE="${BACKUP_RCLONE_REMOTE?BACKUP_RCLONE_REMOTE not set}" # e.g., "myremote:/backup/relab" +BACKUP_RCLONE_MULTI_THREAD_STREAMS="${BACKUP_RCLONE_MULTI_THREAD_STREAMS:-16}" + +# Safety Check: If the local dir has 0 files AND the remote has more than 0 files, abort. +LOCAL_FILE_COUNT=$(find "$BACKUP_DIR" -type f | wc -l) +REMOTE_FILE_COUNT=$(rclone lsf "$BACKUP_RCLONE_REMOTE" --files-only 2>/dev/null | wc -l) + +if [ "$LOCAL_FILE_COUNT" -eq 0 ] && [ "$REMOTE_FILE_COUNT" -gt 0 ]; then + echo "[$(date)] ERROR: Local backup directory is empty, but remote is not. Aborting sync to prevent data loss." + exit 1 +fi + +echo "[$(date)] Safety check passed. Syncing backups to $BACKUP_RCLONE_REMOTE..." +rclone sync "$BACKUP_DIR" "$BACKUP_RCLONE_REMOTE" \ + --multi-thread-streams="$BACKUP_RCLONE_MULTI_THREAD_STREAMS" \ + --copy-links \ + --checksum \ + --transfers="$BACKUP_RCLONE_MULTI_THREAD_STREAMS" \ + --retries 3 \ + --low-level-retries 10 \ + --stats=30s \ + --stats-one-line + +echo "[$(date)] Sync complete." \ No newline at end of file diff --git a/backend/scripts/backup/rsync_backup.sh b/backend/scripts/backup/rsync_backup.sh index 05e3537..2315fb4 100755 --- a/backend/scripts/backup/rsync_backup.sh +++ b/backend/scripts/backup/rsync_backup.sh @@ -16,17 +16,17 @@ fi # Configuration BACKUP_DIR="${BACKUP_DIR:-$REPO_ROOT/backend/backups}" -BACKUP_REMOTE_HOST="${BACKUP_REMOTE_HOST?BACKUP_REMOTE_HOST not set}" -BACKUP_REMOTE_DIR="${BACKUP_REMOTE_DIR?BACKUP_REMOTE_DIR not set}" +BACKUP_RSYNC_REMOTE_HOST="${BACKUP_RSYNC_REMOTE_HOST?BACKUP_RSYNC_REMOTE_HOST not set}" +BACKUP_RSYNC_REMOTE_DIR="${BACKUP_RSYNC_REMOTE_DIR?BACKUP_RSYNC_REMOTE_DIR not set}" # Safety Check: If the local dir has 0 files AND the remote has more than 0 files, abort. if [ "$(find "$BACKUP_DIR" -type f | wc -l)" -eq 0 ] && \ - [ "$(ssh "$BACKUP_REMOTE_HOST" "find '$BACKUP_REMOTE_DIR' -type f 2>/dev/null | wc -l")" -gt 0 ]; then + [ "$(ssh "$BACKUP_RSYNC_REMOTE_HOST" "find '$BACKUP_RSYNC_REMOTE_DIR' -type f 2>/dev/null | wc -l")" -gt 0 ]; then echo "[$(date)] ERROR: Local backup directory is empty, but remote is not. Aborting sync to prevent data loss." exit 1 fi -BACKUP_REMOTE="$BACKUP_REMOTE_HOST:$BACKUP_REMOTE_DIR" +BACKUP_REMOTE="$BACKUP_RSYNC_REMOTE_HOST:$BACKUP_RSYNC_REMOTE_DIR" echo "[$(date)] Safety check passed. Mirroring backups to $BACKUP_REMOTE..." rsync -avz --delete "$BACKUP_DIR"/ "$BACKUP_REMOTE" From c68890fc0850c3ea9d34e6791cc5ff5379172251 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 28 Oct 2025 11:54:13 +0000 Subject: [PATCH 08/79] feat(backend): Print backup stats after rclone --- backend/scripts/backup/rclone_backup.sh | 5 +++-- compose.prod.yml | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/scripts/backup/rclone_backup.sh b/backend/scripts/backup/rclone_backup.sh index da25000..4a5e123 100755 --- a/backend/scripts/backup/rclone_backup.sh +++ b/backend/scripts/backup/rclone_backup.sh @@ -21,7 +21,7 @@ BACKUP_RCLONE_MULTI_THREAD_STREAMS="${BACKUP_RCLONE_MULTI_THREAD_STREAMS:-16}" # Safety Check: If the local dir has 0 files AND the remote has more than 0 files, abort. LOCAL_FILE_COUNT=$(find "$BACKUP_DIR" -type f | wc -l) -REMOTE_FILE_COUNT=$(rclone lsf "$BACKUP_RCLONE_REMOTE" --files-only 2>/dev/null | wc -l) +REMOTE_FILE_COUNT=$(rclone lsf "$BACKUP_RCLONE_REMOTE" --files-only --max-depth=3 2>/dev/null | wc -l) if [ "$LOCAL_FILE_COUNT" -eq 0 ] && [ "$REMOTE_FILE_COUNT" -gt 0 ]; then echo "[$(date)] ERROR: Local backup directory is empty, but remote is not. Aborting sync to prevent data loss." @@ -39,4 +39,5 @@ rclone sync "$BACKUP_DIR" "$BACKUP_RCLONE_REMOTE" \ --stats=30s \ --stats-one-line -echo "[$(date)] Sync complete." \ No newline at end of file +echo "[$(date)] Sync complete. Remote backup stats after sync:" +rclone size "$BACKUP_RCLONE_REMOTE" --max-depth=3 2>/dev/null | sed 's/^/ /' \ No newline at end of file diff --git a/compose.prod.yml b/compose.prod.yml index 656a48f..dc1c5a3 100644 --- a/compose.prod.yml +++ b/compose.prod.yml @@ -46,7 +46,6 @@ services: environment: SCHEDULE: 0 2 * * * # Daily at 2am POSTGRES_HOST: database - BACKUP_ON_STARTUP: true POSTGRES_EXTRA_OPTS: "--compress=zstd:3 --schema=public" # Compress backups, only back up public schema BACKUP_KEEP_DAYS: 7 BACKUP_KEEP_WEEKS: 4 From f2e1eaf1344fff0a094698e3ae386c37c0404ce1 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 28 Oct 2025 13:03:46 +0000 Subject: [PATCH 09/79] fix(backend): Use rclone symlink files for symlinked backups --- backend/scripts/backup/rclone_backup.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/scripts/backup/rclone_backup.sh b/backend/scripts/backup/rclone_backup.sh index 4a5e123..1188c1a 100755 --- a/backend/scripts/backup/rclone_backup.sh +++ b/backend/scripts/backup/rclone_backup.sh @@ -31,13 +31,13 @@ fi echo "[$(date)] Safety check passed. Syncing backups to $BACKUP_RCLONE_REMOTE..." rclone sync "$BACKUP_DIR" "$BACKUP_RCLONE_REMOTE" \ --multi-thread-streams="$BACKUP_RCLONE_MULTI_THREAD_STREAMS" \ - --copy-links \ + --links \ --checksum \ --transfers="$BACKUP_RCLONE_MULTI_THREAD_STREAMS" \ --retries 3 \ --low-level-retries 10 \ --stats=30s \ - --stats-one-line + --stats-one-line-date echo "[$(date)] Sync complete. Remote backup stats after sync:" rclone size "$BACKUP_RCLONE_REMOTE" --max-depth=3 2>/dev/null | sed 's/^/ /' \ No newline at end of file From 87558679a913a56d13adddf0749261f285b1175b Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Wed, 29 Oct 2025 09:39:38 +0000 Subject: [PATCH 10/79] fix(backend): Move username validation from model to schema --- backend/app/api/auth/models.py | 9 +++------ backend/app/api/auth/schemas.py | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/backend/app/api/auth/models.py b/backend/app/api/auth/models.py index 46acc52..5b507f0 100644 --- a/backend/app/api/auth/models.py +++ b/backend/app/api/auth/models.py @@ -3,10 +3,10 @@ import uuid from enum import Enum from functools import cached_property -from typing import TYPE_CHECKING, Annotated, Optional +from typing import TYPE_CHECKING, Optional from fastapi_users_db_sqlmodel import SQLModelBaseOAuthAccount, SQLModelBaseUserDB -from pydantic import UUID4, BaseModel, ConfigDict, StringConstraints +from pydantic import UUID4, BaseModel, ConfigDict from sqlalchemy import Enum as SAEnum from sqlalchemy import ForeignKey from sqlmodel import Column, Field, Relationship @@ -31,10 +31,7 @@ class OrganizationRole(str, Enum): class UserBase(BaseModel): """Base schema for user data.""" - username: Annotated[ - str | None, - StringConstraints(strip_whitespace=True, pattern=r"^[\w]+$"), # Allows only letters, numbers, and underscores - ] = Field(index=True, unique=True, default=None) + username: str | None = Field(index=True, unique=True, default=None, min_length=2, max_length=50) model_config = ConfigDict(use_enum_values=True) # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 diff --git a/backend/app/api/auth/schemas.py b/backend/app/api/auth/schemas.py index 9f69481..6abd33f 100644 --- a/backend/app/api/auth/schemas.py +++ b/backend/app/api/auth/schemas.py @@ -51,14 +51,21 @@ class OrganizationUpdate(BaseUpdateSchema): ### Users ### + +# Validation constraints for username field +ValidatedUsername = Annotated[ + str | None, StringConstraints(strip_whitespace=True, pattern=r"^\w+$", min_length=2, max_length=50) +] + + class UserCreateBase(UserBase, schemas.BaseUserCreate): """Base schema for user creation.""" - # Override for validation - username: Annotated[str | None, StringConstraints(strip_whitespace=True)] = None + # Override for username field validation + username: ValidatedUsername = None # Override for OpenAPI schema configuration - password: str = Field(json_schema_extra={"format": "password"}) + password: str = Field(json_schema_extra={"format": "password"}, min_length=8) class UserCreate(UserCreateBase): @@ -145,11 +152,13 @@ class UserReadWithRelationships(UserReadWithOrganization): class UserUpdate(UserBase, schemas.BaseUserUpdate): """Update schema for users.""" - username: Annotated[str | None, StringConstraints(strip_whitespace=True)] = None + # Override for username field validation + username: ValidatedUsername = None + organization_id: UUID4 | None = None # Override password field to include password format in JSON schema - password: str | None = Field(default=None, json_schema_extra={"format": "password"}) + password: str | None = Field(default=None, json_schema_extra={"format": "password"}, min_length=8) model_config: ConfigDict = ConfigDict( # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 { From 4e7a0acce779db3aaeb43ec8c3f3050540ce051c Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Wed, 29 Oct 2025 10:03:06 +0000 Subject: [PATCH 11/79] refactor(frontend-app): Deduplicate product name validation --- frontend-app/src/app/products/[id]/index.tsx | 70 +++++---- .../components/product/ProductComponents.tsx | 59 ++++--- .../src/services/api/validation/product.ts | 146 +++++++++++++++--- 3 files changed, 198 insertions(+), 77 deletions(-) diff --git a/frontend-app/src/app/products/[id]/index.tsx b/frontend-app/src/app/products/[id]/index.tsx index f240cac..725f55b 100644 --- a/frontend-app/src/app/products/[id]/index.tsx +++ b/frontend-app/src/app/products/[id]/index.tsx @@ -22,7 +22,7 @@ import { useDialog } from '@/components/common/DialogProvider'; import { getProduct, newProduct } from '@/services/api/fetching'; import { deleteProduct, saveProduct } from '@/services/api/saving'; -import { getProductNameHelperText, isProductValid, isValidProductName } from '@/services/api/validation/product'; +import { getProductNameHelperText, validateProduct, validateProductName } from '@/services/api/validation/product'; import { Product } from '@/types/Product'; /** @@ -256,15 +256,15 @@ export default function ProductPage(): JSX.Element { - + ); } @@ -279,27 +279,37 @@ function EditNameButton({ const dialog = useDialog(); const onPress = () => { - if (!product) { - return; - } - dialog.input({ - title: 'Edit name', - placeholder: 'Product Name', - helperText: getProductNameHelperText(), - defaultValue: product.name || '', - buttons: [ - { text: 'Cancel', onPress: () => undefined }, - { - text: 'OK', - disabled: (value) => !isValidProductName(value), - onPress: (newName) => { - const name = typeof newName === 'string' ? newName.trim() : ''; - onProductNameChange?.(name); - }, + if (!product) { + return; + } + dialog.input({ + title: 'Edit name', + placeholder: 'Product Name', + helperText: getProductNameHelperText(), + defaultValue: product.name || '', + buttons: [ + { text: 'Cancel', onPress: () => undefined }, + { + text: 'OK', + disabled: (value) => { + const result = validateProductName(value); + return !result.isValid; }, - ], - }); - }; + onPress: (newName) => { + const name = typeof newName === 'string' ? newName.trim() : ''; + const result = validateProductName(name); + + if (!result.isValid) { + alert(result.error); + return; + } + + onProductNameChange?.(name); + }, + }, + ], + }); +}; return ; } diff --git a/frontend-app/src/components/product/ProductComponents.tsx b/frontend-app/src/components/product/ProductComponents.tsx index 6261606..2acd837 100644 --- a/frontend-app/src/components/product/ProductComponents.tsx +++ b/frontend-app/src/components/product/ProductComponents.tsx @@ -2,13 +2,13 @@ import { useEffect, useState } from 'react'; import { View } from 'react-native'; import { Button } from 'react-native-paper'; -import { useRouter } from 'expo-router'; import { InfoTooltip, Text } from '@/components/base'; import { useDialog } from '@/components/common/DialogProvider'; import ProductCard from '@/components/common/ProductCard'; import { productComponents } from '@/services/api/fetching'; -import { isValidProductName } from '@/services/api/validation/product'; +import { getProductNameHelperText, validateProductName } from '@/services/api/validation/product'; import { Product } from '@/types/Product'; +import { useRouter } from 'expo-router'; interface Props { product: Product; @@ -30,29 +30,40 @@ export default function ProductComponents({ product, editMode }: Props) { // Callbacks const newComponent = () => { - dialog.input({ - title: 'Create New Component', - placeholder: 'Component Name', - helperText: 'Enter a descriptive name between 2 and 100 characters', - buttons: [ - { text: 'Cancel' }, - { - text: 'OK', - disabled: (value) => !isValidProductName(value), - onPress: (componentName) => { - const name = typeof componentName === 'string' ? componentName.trim() : ''; - const params = { - id: 'new', - name, - isComponent: 'true', - parent: product.id, - }; - router.push({ pathname: '/products/[id]', params: params }); - }, + dialog.input({ + title: 'Create New Component', + placeholder: 'Component Name', + helperText: getProductNameHelperText(), + buttons: [ + { text: 'Cancel' }, + { + text: 'OK', + disabled: (value) => { + const result = validateProductName(value); + return !result.isValid; + }, + onPress: (componentName) => { + const name = typeof componentName === 'string' ? componentName.trim() : ''; + const result = validateProductName(name); + + if (!result.isValid) { + // This shouldn't happen due to disabled check, but handle defensively + alert(result.error); + return; + } + + const params = { + id: 'new', + name, + isComponent: 'true', + parent: product.id, + }; + router.push({ pathname: '/products/[id]', params: params }); }, - ], - }); - }; + }, + ], + }); +}; // Render return ( diff --git a/frontend-app/src/services/api/validation/product.ts b/frontend-app/src/services/api/validation/product.ts index 37c9658..39e2ba8 100644 --- a/frontend-app/src/services/api/validation/product.ts +++ b/frontend-app/src/services/api/validation/product.ts @@ -7,9 +7,33 @@ import { Product } from '@/types/Product'; export const PRODUCT_NAME_MIN_LENGTH = 2; export const PRODUCT_NAME_MAX_LENGTH = 100; -export function isValidProductName(value: string | undefined): boolean { +export type ValidationResult = { + isValid: boolean; + error?: string; +}; + +export function validateProductName(value: string | undefined): ValidationResult { const name = typeof value === 'string' ? value.trim() : ''; - return name.length >= PRODUCT_NAME_MIN_LENGTH && name.length <= PRODUCT_NAME_MAX_LENGTH; + + if (!name) { + return { isValid: false, error: 'Product name is required' }; + } + + if (name.length < PRODUCT_NAME_MIN_LENGTH) { + return { + isValid: false, + error: `Product name must be at least ${PRODUCT_NAME_MIN_LENGTH} characters` + }; + } + + if (name.length > PRODUCT_NAME_MAX_LENGTH) { + return { + isValid: false, + error: `Product name must be at most ${PRODUCT_NAME_MAX_LENGTH} characters` + }; + } + + return { isValid: true }; } export function getProductNameHelperText(): string { @@ -31,27 +55,103 @@ export function isValidUrl(value: string | undefined): boolean { } } -export function isProductValid(product: Product): boolean { +export function isValidUrl(value: string | undefined): boolean { + if (!value || typeof value !== 'string') return false; + + const trimmed = value.trim(); + if (trimmed.length === 0) return false; + + try { + const url = new URL(trimmed); + // Check if protocol is http or https + return url.protocol === 'http:' || url.protocol === 'https:'; + } catch { + return false; + } +} + +export function validateProductDimension( + value: number | undefined, + dimensionName: string +): ValidationResult { + if (value == null || Number.isNaN(value)) { + return { isValid: true }; // Optional field + } + + if (typeof value !== 'number' || value <= 0) { + return { + isValid: false, + error: `${dimensionName} must be a positive number` + }; + } + + return { isValid: true }; +} + +export function validateProductWeight(value: number | undefined): ValidationResult { + if (value == null || Number.isNaN(value)) { + return { isValid: false, error: 'Weight is required' }; + } + + if (typeof value !== 'number' || value <= 0) { + return { isValid: false, error: 'Weight must be a positive number' }; + } + + return { isValid: true }; +} + +export function validateProductVideos(videos: { title: string; url: string }[]): ValidationResult { + for (const video of videos) { + if (video.title.trim().length === 0) { + return { isValid: false, error: 'Video title cannot be empty' }; + } + if (!isValidUrl(video.url)) { + return { isValid: false, error: `Invalid URL for video titled "${video.title}"` }; + } + } + return { isValid: true }; +} + +export function validateProduct(product: Product): ValidationResult { const { weight, width, height, depth } = product.physicalProperties; - // Allow undefined dimensions, but if provided, they must be positive numbers - const isValidDimension = (val: number | undefined) => { - return val == null || Number.isNaN(val) || (typeof val === 'number' && val > 0); - }; - - // Validate that all videos have non-empty titles and valid URLs - const areVideosValid = product.videos.every(video => { - return video.title.trim().length > 0 && isValidUrl(video.url); - }); - - return ( - isValidProductName(product.name) && - typeof weight === 'number' && - !Number.isNaN(weight) && - weight > 0 && - isValidDimension(width) && - isValidDimension(height) && - isValidDimension(depth) && - areVideosValid - ); + // Validate product name + const nameResult = validateProductName(product.name); + if (!nameResult.isValid) { + return nameResult; + } + + // Validate weight + const weightResult = validateProductWeight(weight); + if (!weightResult.isValid) { + return weightResult; + } + + // Validate dimensions + const widthResult = validateProductDimension(width, 'Width'); + if (!widthResult.isValid) { + return widthResult; + } + + const heightResult = validateProductDimension(height, 'Height'); + if (!heightResult.isValid) { + return heightResult; + } + + const depthResult = validateProductDimension(depth, 'Depth'); + if (!depthResult.isValid) { + return depthResult; + } + + // Validate product videos + const videosResult = validateProductVideos(product.videos); + if (!videosResult.isValid) { + return videosResult; + } + + return { isValid: true }; +} + +export function isProductValid(product: Product): boolean { + return validateProduct(product).isValid; } From 93a938ed6f4d2abf1f70fc4f93f467a339c83b76 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Wed, 29 Oct 2025 10:03:24 +0000 Subject: [PATCH 12/79] chore(deps): update pre-commit yaml --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 69be1fe..cfb56f1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -57,14 +57,14 @@ repos: files: ^ (?!(backend/frontend-app|frontend-web)/data/) ### Backend hooks - repo: https://github.com/RobertCraigie/pyright-python # Lint backend code with Pyright. - rev: v1.1.406 + rev: v1.1.407 hooks: - id: pyright files: ^backend/(app|scripts|tests)/ entry: pyright --project backend - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.1 + rev: v0.14.2 hooks: - id: ruff-check # Lint code files: ^backend/(app|scripts|tests)/ @@ -74,7 +74,7 @@ repos: args: ["--config", "backend/pyproject.toml"] - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.4 + rev: 0.9.5 hooks: - id: uv-lock # Update the uv lockfile for the backend. files: ^backend/(uv\.lock|pyproject\.toml|uv\.toml)$ From 5875e1ee8007e8ca83373d916b4d0fa87aaa1ce0 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Wed, 29 Oct 2025 10:03:50 +0000 Subject: [PATCH 13/79] feat(frontend-app): update registration validation and error handling --- frontend-app/src/app/(auth)/new-account.tsx | 419 ++++++++++-------- .../src/services/api/authentication.ts | 31 +- .../src/services/api/validation/user.ts | 125 ++++++ 3 files changed, 376 insertions(+), 199 deletions(-) create mode 100644 frontend-app/src/services/api/validation/user.ts diff --git a/frontend-app/src/app/(auth)/new-account.tsx b/frontend-app/src/app/(auth)/new-account.tsx index e5f0ce5..ed178bf 100644 --- a/frontend-app/src/app/(auth)/new-account.tsx +++ b/frontend-app/src/app/(auth)/new-account.tsx @@ -1,168 +1,227 @@ import { Link, useRouter } from 'expo-router'; import { useState } from 'react'; -import { View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { Button, HelperText, IconButton, Text, TextInput } from 'react-native-paper'; -import validator from 'validator'; import { login, register } from '@/services/api/authentication'; +import { + validateEmail, + validatePassword, + validateUsername +} from '@/services/api/validation/user'; + +const styles = StyleSheet.create({ + container: { + flex: 1, + padding: 20, + }, + welcomeText: { + marginTop: 80, + fontSize: 40, + marginLeft: 5, + }, + brandText: { + fontSize: 80, + fontWeight: 'bold', + }, + questionText: { + fontSize: 31, + marginTop: 80, + marginLeft: 5, + marginBottom: 40, + }, + inputContainer: { + flexDirection: 'column', + marginBottom: 10, + }, + inputRow: { + flexDirection: 'row', + alignItems: 'center', + }, + textInput: { + flex: 1, + marginRight: 10, + }, + helperText: { + marginTop: -8, + }, + backButton: { + flexDirection: 'row', + alignItems: 'center', + marginTop: 8, + }, + backButtonIcon: { + margin: 0, + }, + backButtonText: { + fontSize: 13, + color: '#999', + marginLeft: 4, + }, + bottomContainer: { + position: 'absolute', + bottom: 20, + left: 20, + right: 20, + alignItems: 'center', + gap: 8, + }, + privacyText: { + fontSize: 12, + opacity: 0.7, + textAlign: 'center', + }, + privacyLink: { + fontSize: 12, + textDecorationLine: 'underline', + }, + registerButton: { + minWidth: 140, + }, +}); + +const PrivacyPolicy = () => ( + + By creating an account, you agree to our{' '} + + Privacy Policy + + +); export default function NewAccount() { - // Hooks const router = useRouter(); - // States const [section, setSection] = useState<'username' | 'email' | 'password'>('username'); - const [username, setUsername] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); + const [usernameError, setUsernameError] = useState(''); const [emailError, setEmailError] = useState(''); + const [passwordError, setPasswordError] = useState(''); + const [isRegistering, setIsRegistering] = useState(false); - // Functions - const validateEmail = async (emailInput: string) => { - setEmail(emailInput); - setEmailError(''); + const handleUsernameChange = (input: string) => { + const trimmed = input.trim(); + setUsername(trimmed); + + const result = validateUsername(trimmed); + setUsernameError(result.error || ''); + }; + + const handleEmailChange = (input: string) => { + setEmail(input); + + const result = validateEmail(input); + setEmailError(result.error || ''); + }; - if (!emailInput) { + const handlePasswordChange = (input: string) => { + setPassword(input); + + const result = validatePassword(input, username, email); + setPasswordError(result.error || ''); + }; + + const createAccount = async () => { + // Final validation + const usernameResult = validateUsername(username); + if (!usernameResult.isValid) { + alert(usernameResult.error); return; } - // Check if email format is valid - if (!validator.isEmail(emailInput)) { - setEmailError('Please enter a valid email address'); + const emailResult = validateEmail(email); + if (!emailResult.isValid) { + alert(emailResult.error); return; } - // Check for disposable email via backend - try { - const response = await fetch( - `${process.env.EXPO_PUBLIC_API_URL}/auth/validate-email?email=${encodeURIComponent(emailInput)}`, - ); - const data = await response.json(); - - if (!data.isValid) { - setEmailError(data.reason || 'Please use a permanent email address'); - return; - } - } catch (error) { - console.error('Error validating email:', error); - // Continue even if the check fails - don't block the user + const passwordResult = validatePassword(password, username, email); + if (!passwordResult.isValid) { + alert(passwordResult.error); + return; } - }; - const createAccount = async () => { - const success = await register(username, email, password); - if (!success) { - alert('Account creation failed. Please try again.'); + setIsRegistering(true); + + const result = await register(username, email, password); + + if (!result.success) { + setIsRegistering(false); + alert(result.error || 'Account creation failed. Please try again.'); return; } + const loginSuccess = await login(email, password); + setIsRegistering(false); + if (!loginSuccess) { - alert('Login failed. Please try logging in manually.'); + alert('Account created! Please log in manually.'); router.replace('/login'); return; } + router.navigate('/products'); }; - // Render if (section === 'username') { return ( - - - {'Welcome to'} - - - {'ReLab.'} - - - {'Who are you?'} - + + Welcome to + ReLab. + Who are you? - - - setSection('email')} - /> + + + + setSection('email')} + /> + + {usernameError && ( + + {usernameError} + + )} + + + + + - ); } + if (section === 'email') { return ( - - - {'Hi'} - - - {username + '.'} - - - {'How do we reach you?'} - + + Hi + {username}. + How do we reach you? - - + + setSection('password')} /> - {emailError ? ( - + {emailError && ( + {emailError} - ) : null} + )} - + setSection('username')} - style={{ margin: 0 }} + style={styles.backButtonIcon} /> - setSection('username')} style={{ fontSize: 13, color: '#999', marginLeft: 4 }}> + setSection('username')} style={styles.backButtonText}> Edit username - + + + + ); } if (section === 'password') { return ( - - - {'Finally,'} - - - {username + '.'} - - - {'How will you log in?'} - + + Finally, + {username}. + How will you log in? - - - + + + + + + {passwordError && ( + + {passwordError} + + )} - + setSection('email')} - style={{ margin: 0 }} + style={styles.backButtonIcon} /> - setSection('email')} style={{ fontSize: 13, color: '#999', marginLeft: 4 }}> + setSection('email')} style={styles.backButtonText}> Edit email address - - - By creating an account, you agree to our{' '} - - Privacy Policy - - - - ); } -} +} \ No newline at end of file diff --git a/frontend-app/src/services/api/authentication.ts b/frontend-app/src/services/api/authentication.ts index e7973ff..a36399b 100644 --- a/frontend-app/src/services/api/authentication.ts +++ b/frontend-app/src/services/api/authentication.ts @@ -1,12 +1,12 @@ -import AsyncStorage from '@react-native-async-storage/async-storage'; import { User } from '@/types/User'; +import AsyncStorage from '@react-native-async-storage/async-storage'; -const baseUrl = `${process.env.EXPO_PUBLIC_API_URL}`; +const apiURL = `${process.env.EXPO_PUBLIC_API_URL}`; let token: string | undefined; let user: User | undefined; export async function login(username: string, password: string): Promise { - const url = new URL(baseUrl + '/auth/bearer/login'); + const url = new URL(apiURL + '/auth/bearer/login'); const headers = { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' }; const body = new URLSearchParams({ username, password }).toString(); @@ -74,7 +74,7 @@ export async function getUser(): Promise { return user; } - const url = new URL(baseUrl + '/users/me'); + const url = new URL(apiURL + '/users/me'); const authToken = await getToken(); if (!authToken) { return undefined; @@ -112,8 +112,8 @@ export async function getUser(): Promise { } } -export async function register(username: string, email: string, password: string): Promise { - const url = new URL(baseUrl + '/auth/register'); +export async function register(username: string, email: string, password: string): Promise<{ success: boolean; error?: string }> { + const url = new URL(apiURL + '/auth/register'); const headers = { 'Content-Type': 'application/json', Accept: 'application/json' }; const body = { @@ -122,12 +122,25 @@ export async function register(username: string, email: string, password: string password: password, }; - const response = await fetch(url, { method: 'POST', headers: headers, body: JSON.stringify(body) }); - return response.ok; + try { + const response = await fetch(url, { method: 'POST', headers: headers, body: JSON.stringify(body) }); + + if (response.ok) { + return { success: true }; + } + + const errorData = await response.json(); + const errorMessage = errorData.detail?.reason || errorData.detail || 'Registration failed. Please try again.'; + + return { success: false, error: errorMessage }; + } catch (error) { + console.error('Registration error:', error); + return { success: false, error: 'Network error. Please check your connection and try again.' }; + } } export async function verify(email: string): Promise { - const url = new URL(baseUrl + '/auth/request-verify-token'); + const url = new URL(apiURL + '/auth/request-verify-token'); const headers = { 'Content-Type': 'application/json', Accept: 'application/json' }; const body = { diff --git a/frontend-app/src/services/api/validation/user.ts b/frontend-app/src/services/api/validation/user.ts new file mode 100644 index 0000000..c81b3b8 --- /dev/null +++ b/frontend-app/src/services/api/validation/user.ts @@ -0,0 +1,125 @@ +/** + * User validation utilities + */ + +// Constants +export const USERNAME_MIN_LENGTH = 2; +export const USERNAME_MAX_LENGTH = 50; +export const USERNAME_PATTERN = /^\w+$/; // Only letters, numbers, and underscores + +export const PASSWORD_MIN_LENGTH = 8; +export const PASSWORD_MAX_LENGTH = 128; + +// Types +export type ValidationResult = { + isValid: boolean; + error?: string; +}; + +// Methods +export function validateUsername(value: string | undefined): ValidationResult { + const username = typeof value === 'string' ? value.trim() : ''; + + if (!username) { + return { isValid: false, error: 'Username is required' }; + } + + if (username.length < USERNAME_MIN_LENGTH) { + return { + isValid: false, + error: `Username must be at least ${USERNAME_MIN_LENGTH} characters` + }; + } + + if (username.length > USERNAME_MAX_LENGTH) { + return { + isValid: false, + error: `Username must be at most ${USERNAME_MAX_LENGTH} characters` + }; + } + + if (!USERNAME_PATTERN.test(username)) { + return { + isValid: false, + error: 'Username can only contain letters, numbers, and underscores' + }; + } + + return { isValid: true }; +} + +export function getUsernameHelperText(): string { + return `Username must be ${USERNAME_MIN_LENGTH}-${USERNAME_MAX_LENGTH} characters and contain only letters, numbers, and underscores`; +} + +export function validateEmail(value: string | undefined): ValidationResult { + const email = typeof value === 'string' ? value.trim() : ''; + + if (!email) { + return { isValid: false, error: 'Email is required' }; + } + + // Basic email format validation + const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailPattern.test(email)) { + return { isValid: false, error: 'Please enter a valid email address' }; + } + + return { isValid: true }; +} + +export function getEmailHelperText(): string { + return 'Enter a valid email address'; +} + +export function validatePassword( + value: string | undefined, + username?: string, + email?: string +): ValidationResult { + const password = typeof value === 'string' ? value : ''; + + if (!password) { + return { isValid: false, error: 'Password is required' }; + } + + if (password.length < PASSWORD_MIN_LENGTH) { + return { + isValid: false, + error: `Password must be at least ${PASSWORD_MIN_LENGTH} characters` + }; + } + + if (password.length > PASSWORD_MAX_LENGTH) { + return { + isValid: false, + error: `Password must be at most ${PASSWORD_MAX_LENGTH} characters` + }; + } + + // Check if password contains username + if (username && password.toLowerCase().includes(username.toLowerCase())) { + return { + isValid: false, + error: 'Password cannot contain your username' + }; + } + + // Check if password contains email or email username part + if (email) { + const emailUsername = email.split('@')[0]; + if (password.toLowerCase().includes(email.toLowerCase()) || + password.toLowerCase().includes(emailUsername.toLowerCase())) { + return { + isValid: false, + error: 'Password cannot contain your email address' + }; + } + } + + return { isValid: true }; +} + +export function getPasswordHelperText(): string { + return `Password must be ${PASSWORD_MIN_LENGTH}-${PASSWORD_MAX_LENGTH} characters and cannot contain your username or email`; +} From acdd1f0b8cbf81ccd86c29a3dc515f12346fc375 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Wed, 29 Oct 2025 11:27:29 +0100 Subject: [PATCH 14/79] fix(cicd): Format frontend instead of just linting --- .pre-commit-config.yaml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cfb56f1..ea48998 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -55,7 +55,8 @@ repos: hooks: - id: check-json5 files: ^ (?!(backend/frontend-app|frontend-web)/data/) - ### Backend hooks + + ### Backend hooks - repo: https://github.com/RobertCraigie/pyright-python # Lint backend code with Pyright. rev: v1.1.407 hooks: @@ -90,22 +91,22 @@ repos: pass_filenames: false stages: [pre-commit] - ### Frontend hooks + ### Frontend hooks - repo: local hooks: - - id: frontend-web-lint - name: lint frontend-web code - entry: bash -c 'cd frontend-web && npm run lint' + - id: frontend-web-format + name: format frontend-web code + entry: bash -c 'cd frontend-web && npm run format' language: system - # Match frontend JavaScript and TypeScript files for linting. + # Match frontend JavaScript and TypeScript files for formatting. files: ^frontend-web\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ pass_filenames: false - - id: frontend-app-lint - name: lint frontend-app code - entry: bash -c 'cd frontend-app && npm run lint' + - id: frontend-app-format + name: format frontend-app code + entry: bash -c 'cd frontend-app && npm run format' language: system - # Match frontend JavaScript and TypeScript files for linting. + # Match frontend JavaScript and TypeScript files for formatting. files: ^frontend-app\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ pass_filenames: false From 4b5e80bb1d352b479d90923b5a71019bdc2f9299 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Wed, 29 Oct 2025 11:46:33 +0100 Subject: [PATCH 15/79] chore: linting --- .env.example | 2 +- .../app/api/auth/utils/programmatic_emails.py | 1 - backend/scripts/backup/README.md | 6 +- backend/scripts/backup/rclone_backup.sh | 4 +- backend/scripts/seed/migrations_entrypoint.sh | 1 - frontend-app/src/app/(auth)/new-account.tsx | 38 ++++------ frontend-app/src/app/products/[id]/index.tsx | 76 +++++++++---------- .../components/product/ProductComponents.tsx | 64 ++++++++-------- .../src/services/api/authentication.ts | 14 ++-- .../src/services/api/validation/product.ts | 26 +------ .../src/services/api/validation/user.ts | 26 +++---- 11 files changed, 115 insertions(+), 143 deletions(-) diff --git a/.env.example b/.env.example index 00aa2f7..57e0a49 100644 --- a/.env.example +++ b/.env.example @@ -15,4 +15,4 @@ BACKUP_RSYNC_REMOTE_PATH=/path/to/remote/backup # Remote rclone backup config (for use of backend/scripts/backup/rclone_backup.sh script) BACKUP_RCLONE_REMOTE=myremote:/path/to/remote/backup -BACKUP_RCLONE_MULTI_THREAD_STREAMS=16 \ No newline at end of file +BACKUP_RCLONE_MULTI_THREAD_STREAMS=16 diff --git a/backend/app/api/auth/utils/programmatic_emails.py b/backend/app/api/auth/utils/programmatic_emails.py index dc1cefa..e9bef95 100644 --- a/backend/app/api/auth/utils/programmatic_emails.py +++ b/backend/app/api/auth/utils/programmatic_emails.py @@ -68,7 +68,6 @@ async def send_email( port=auth_settings.email_port, ) await smtp.connect() - # logger.info("Sending email to %s", auth_settings.__dict__) await smtp.login(auth_settings.email_username, auth_settings.email_password) await smtp.send_message(msg) await smtp.quit() diff --git a/backend/scripts/backup/README.md b/backend/scripts/backup/README.md index e180501..abeb00f 100644 --- a/backend/scripts/backup/README.md +++ b/backend/scripts/backup/README.md @@ -11,7 +11,7 @@ Two types of data are backed up: Backups are created locally first, then optionally synced to remote storage. ---- +______________________________________________________________________ ## Local Backups @@ -61,7 +61,7 @@ This runs: Backup schedules and retention policies are configured in [`compose.prod.yml`](../../../compose.prod.yml). ---- +______________________________________________________________________ ## Remote Backups @@ -100,7 +100,7 @@ BACKUP_RSYNC_REMOTE_PATH=/path/to/remote/backup 30 3 * * * /path/to/relab/backend/scripts/backup/rsync_backup.sh >> /var/log/relab/rsync_backup.log 2>&1 ``` ---- +______________________________________________________________________ ### Option 2: rclone (Cloud/SFTP) diff --git a/backend/scripts/backup/rclone_backup.sh b/backend/scripts/backup/rclone_backup.sh index 1188c1a..16d9db3 100755 --- a/backend/scripts/backup/rclone_backup.sh +++ b/backend/scripts/backup/rclone_backup.sh @@ -37,7 +37,7 @@ rclone sync "$BACKUP_DIR" "$BACKUP_RCLONE_REMOTE" \ --retries 3 \ --low-level-retries 10 \ --stats=30s \ - --stats-one-line-date + --stats-one-line-date echo "[$(date)] Sync complete. Remote backup stats after sync:" -rclone size "$BACKUP_RCLONE_REMOTE" --max-depth=3 2>/dev/null | sed 's/^/ /' \ No newline at end of file +rclone size "$BACKUP_RCLONE_REMOTE" --max-depth=3 2>/dev/null | sed 's/^/ /' diff --git a/backend/scripts/seed/migrations_entrypoint.sh b/backend/scripts/seed/migrations_entrypoint.sh index e4fd77b..4124d6e 100755 --- a/backend/scripts/seed/migrations_entrypoint.sh +++ b/backend/scripts/seed/migrations_entrypoint.sh @@ -49,4 +49,3 @@ echo "Creating a superuser..." # Start the server or other desired commands exec "$@" -# ...existing code... \ No newline at end of file diff --git a/frontend-app/src/app/(auth)/new-account.tsx b/frontend-app/src/app/(auth)/new-account.tsx index ed178bf..6dce0bb 100644 --- a/frontend-app/src/app/(auth)/new-account.tsx +++ b/frontend-app/src/app/(auth)/new-account.tsx @@ -4,11 +4,7 @@ import { StyleSheet, View } from 'react-native'; import { Button, HelperText, IconButton, Text, TextInput } from 'react-native-paper'; import { login, register } from '@/services/api/authentication'; -import { - validateEmail, - validatePassword, - validateUsername -} from '@/services/api/validation/user'; +import { validateEmail, validatePassword, validateUsername } from '@/services/api/validation/user'; const styles = StyleSheet.create({ container: { @@ -104,21 +100,21 @@ export default function NewAccount() { const handleUsernameChange = (input: string) => { const trimmed = input.trim(); setUsername(trimmed); - + const result = validateUsername(trimmed); setUsernameError(result.error || ''); }; const handleEmailChange = (input: string) => { setEmail(input); - + const result = validateEmail(input); setEmailError(result.error || ''); }; const handlePasswordChange = (input: string) => { setPassword(input); - + const result = validatePassword(input, username, email); setPasswordError(result.error || ''); }; @@ -146,22 +142,22 @@ export default function NewAccount() { setIsRegistering(true); const result = await register(username, email, password); - + if (!result.success) { setIsRegistering(false); alert(result.error || 'Account creation failed. Please try again.'); return; } - + const loginSuccess = await login(email, password); setIsRegistering(false); - + if (!loginSuccess) { alert('Account created! Please log in manually.'); router.replace('/login'); return; } - + router.navigate('/products'); }; @@ -200,14 +196,12 @@ export default function NewAccount() { - + ); } - + if (section === 'email') { return ( @@ -257,9 +251,7 @@ export default function NewAccount() { - + ); @@ -284,7 +276,7 @@ export default function NewAccount() { placeholder="Password" error={!!passwordError} /> - + ); } -} \ No newline at end of file +} diff --git a/frontend-app/src/app/products/[id]/index.tsx b/frontend-app/src/app/products/[id]/index.tsx index 725f55b..3b57952 100644 --- a/frontend-app/src/app/products/[id]/index.tsx +++ b/frontend-app/src/app/products/[id]/index.tsx @@ -256,15 +256,15 @@ export default function ProductPage(): JSX.Element { - + ); } @@ -279,37 +279,37 @@ function EditNameButton({ const dialog = useDialog(); const onPress = () => { - if (!product) { - return; - } - dialog.input({ - title: 'Edit name', - placeholder: 'Product Name', - helperText: getProductNameHelperText(), - defaultValue: product.name || '', - buttons: [ - { text: 'Cancel', onPress: () => undefined }, - { - text: 'OK', - disabled: (value) => { - const result = validateProductName(value); - return !result.isValid; - }, - onPress: (newName) => { - const name = typeof newName === 'string' ? newName.trim() : ''; - const result = validateProductName(name); - - if (!result.isValid) { - alert(result.error); - return; - } + if (!product) { + return; + } + dialog.input({ + title: 'Edit name', + placeholder: 'Product Name', + helperText: getProductNameHelperText(), + defaultValue: product.name || '', + buttons: [ + { text: 'Cancel', onPress: () => undefined }, + { + text: 'OK', + disabled: (value) => { + const result = validateProductName(value); + return !result.isValid; + }, + onPress: (newName) => { + const name = typeof newName === 'string' ? newName.trim() : ''; + const result = validateProductName(name); + + if (!result.isValid) { + alert(result.error); + return; + } - onProductNameChange?.(name); + onProductNameChange?.(name); + }, }, - }, - ], - }); -}; + ], + }); + }; return ; } diff --git a/frontend-app/src/components/product/ProductComponents.tsx b/frontend-app/src/components/product/ProductComponents.tsx index 2acd837..daf9286 100644 --- a/frontend-app/src/components/product/ProductComponents.tsx +++ b/frontend-app/src/components/product/ProductComponents.tsx @@ -2,13 +2,13 @@ import { useEffect, useState } from 'react'; import { View } from 'react-native'; import { Button } from 'react-native-paper'; +import { useRouter } from 'expo-router'; import { InfoTooltip, Text } from '@/components/base'; import { useDialog } from '@/components/common/DialogProvider'; import ProductCard from '@/components/common/ProductCard'; import { productComponents } from '@/services/api/fetching'; import { getProductNameHelperText, validateProductName } from '@/services/api/validation/product'; import { Product } from '@/types/Product'; -import { useRouter } from 'expo-router'; interface Props { product: Product; @@ -30,40 +30,40 @@ export default function ProductComponents({ product, editMode }: Props) { // Callbacks const newComponent = () => { - dialog.input({ - title: 'Create New Component', - placeholder: 'Component Name', - helperText: getProductNameHelperText(), - buttons: [ - { text: 'Cancel' }, - { - text: 'OK', - disabled: (value) => { - const result = validateProductName(value); - return !result.isValid; - }, - onPress: (componentName) => { - const name = typeof componentName === 'string' ? componentName.trim() : ''; - const result = validateProductName(name); + dialog.input({ + title: 'Create New Component', + placeholder: 'Component Name', + helperText: getProductNameHelperText(), + buttons: [ + { text: 'Cancel' }, + { + text: 'OK', + disabled: (value) => { + const result = validateProductName(value); + return !result.isValid; + }, + onPress: (componentName) => { + const name = typeof componentName === 'string' ? componentName.trim() : ''; + const result = validateProductName(name); - if (!result.isValid) { - // This shouldn't happen due to disabled check, but handle defensively - alert(result.error); - return; - } + if (!result.isValid) { + // This shouldn't happen due to disabled check, but handle defensively + alert(result.error); + return; + } - const params = { - id: 'new', - name, - isComponent: 'true', - parent: product.id, - }; - router.push({ pathname: '/products/[id]', params: params }); + const params = { + id: 'new', + name, + isComponent: 'true', + parent: product.id, + }; + router.push({ pathname: '/products/[id]', params: params }); + }, }, - }, - ], - }); -}; + ], + }); + }; // Render return ( diff --git a/frontend-app/src/services/api/authentication.ts b/frontend-app/src/services/api/authentication.ts index a36399b..f720cc3 100644 --- a/frontend-app/src/services/api/authentication.ts +++ b/frontend-app/src/services/api/authentication.ts @@ -1,5 +1,5 @@ -import { User } from '@/types/User'; import AsyncStorage from '@react-native-async-storage/async-storage'; +import { User } from '@/types/User'; const apiURL = `${process.env.EXPO_PUBLIC_API_URL}`; let token: string | undefined; @@ -112,7 +112,11 @@ export async function getUser(): Promise { } } -export async function register(username: string, email: string, password: string): Promise<{ success: boolean; error?: string }> { +export async function register( + username: string, + email: string, + password: string, +): Promise<{ success: boolean; error?: string }> { const url = new URL(apiURL + '/auth/register'); const headers = { 'Content-Type': 'application/json', Accept: 'application/json' }; @@ -124,14 +128,14 @@ export async function register(username: string, email: string, password: string try { const response = await fetch(url, { method: 'POST', headers: headers, body: JSON.stringify(body) }); - + if (response.ok) { return { success: true }; } - + const errorData = await response.json(); const errorMessage = errorData.detail?.reason || errorData.detail || 'Registration failed. Please try again.'; - + return { success: false, error: errorMessage }; } catch (error) { console.error('Registration error:', error); diff --git a/frontend-app/src/services/api/validation/product.ts b/frontend-app/src/services/api/validation/product.ts index 39e2ba8..fd7e7c5 100644 --- a/frontend-app/src/services/api/validation/product.ts +++ b/frontend-app/src/services/api/validation/product.ts @@ -22,14 +22,14 @@ export function validateProductName(value: string | undefined): ValidationResult if (name.length < PRODUCT_NAME_MIN_LENGTH) { return { isValid: false, - error: `Product name must be at least ${PRODUCT_NAME_MIN_LENGTH} characters` + error: `Product name must be at least ${PRODUCT_NAME_MIN_LENGTH} characters`, }; } if (name.length > PRODUCT_NAME_MAX_LENGTH) { return { isValid: false, - error: `Product name must be at most ${PRODUCT_NAME_MAX_LENGTH} characters` + error: `Product name must be at most ${PRODUCT_NAME_MAX_LENGTH} characters`, }; } @@ -55,25 +55,7 @@ export function isValidUrl(value: string | undefined): boolean { } } -export function isValidUrl(value: string | undefined): boolean { - if (!value || typeof value !== 'string') return false; - - const trimmed = value.trim(); - if (trimmed.length === 0) return false; - - try { - const url = new URL(trimmed); - // Check if protocol is http or https - return url.protocol === 'http:' || url.protocol === 'https:'; - } catch { - return false; - } -} - -export function validateProductDimension( - value: number | undefined, - dimensionName: string -): ValidationResult { +export function validateProductDimension(value: number | undefined, dimensionName: string): ValidationResult { if (value == null || Number.isNaN(value)) { return { isValid: true }; // Optional field } @@ -81,7 +63,7 @@ export function validateProductDimension( if (typeof value !== 'number' || value <= 0) { return { isValid: false, - error: `${dimensionName} must be a positive number` + error: `${dimensionName} must be a positive number`, }; } diff --git a/frontend-app/src/services/api/validation/user.ts b/frontend-app/src/services/api/validation/user.ts index c81b3b8..7ed6057 100644 --- a/frontend-app/src/services/api/validation/user.ts +++ b/frontend-app/src/services/api/validation/user.ts @@ -27,21 +27,21 @@ export function validateUsername(value: string | undefined): ValidationResult { if (username.length < USERNAME_MIN_LENGTH) { return { isValid: false, - error: `Username must be at least ${USERNAME_MIN_LENGTH} characters` + error: `Username must be at least ${USERNAME_MIN_LENGTH} characters`, }; } if (username.length > USERNAME_MAX_LENGTH) { return { isValid: false, - error: `Username must be at most ${USERNAME_MAX_LENGTH} characters` + error: `Username must be at most ${USERNAME_MAX_LENGTH} characters`, }; } if (!USERNAME_PATTERN.test(username)) { return { isValid: false, - error: 'Username can only contain letters, numbers, and underscores' + error: 'Username can only contain letters, numbers, and underscores', }; } @@ -72,11 +72,7 @@ export function getEmailHelperText(): string { return 'Enter a valid email address'; } -export function validatePassword( - value: string | undefined, - username?: string, - email?: string -): ValidationResult { +export function validatePassword(value: string | undefined, username?: string, email?: string): ValidationResult { const password = typeof value === 'string' ? value : ''; if (!password) { @@ -86,14 +82,14 @@ export function validatePassword( if (password.length < PASSWORD_MIN_LENGTH) { return { isValid: false, - error: `Password must be at least ${PASSWORD_MIN_LENGTH} characters` + error: `Password must be at least ${PASSWORD_MIN_LENGTH} characters`, }; } if (password.length > PASSWORD_MAX_LENGTH) { return { isValid: false, - error: `Password must be at most ${PASSWORD_MAX_LENGTH} characters` + error: `Password must be at most ${PASSWORD_MAX_LENGTH} characters`, }; } @@ -101,18 +97,20 @@ export function validatePassword( if (username && password.toLowerCase().includes(username.toLowerCase())) { return { isValid: false, - error: 'Password cannot contain your username' + error: 'Password cannot contain your username', }; } // Check if password contains email or email username part if (email) { const emailUsername = email.split('@')[0]; - if (password.toLowerCase().includes(email.toLowerCase()) || - password.toLowerCase().includes(emailUsername.toLowerCase())) { + if ( + password.toLowerCase().includes(email.toLowerCase()) || + password.toLowerCase().includes(emailUsername.toLowerCase()) + ) { return { isValid: false, - error: 'Password cannot contain your email address' + error: 'Password cannot contain your email address', }; } } From cd06b0279a0762a205f06bb31a65924f486c77ef Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Wed, 29 Oct 2025 12:14:12 +0000 Subject: [PATCH 16/79] fix(backend): remove logging info --- backend/app/api/auth/services/user_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/api/auth/services/user_manager.py b/backend/app/api/auth/services/user_manager.py index daad8b7..0eedec0 100644 --- a/backend/app/api/auth/services/user_manager.py +++ b/backend/app/api/auth/services/user_manager.py @@ -139,7 +139,7 @@ async def on_after_verify(self, user: User, request: Request | None = None) -> N await send_post_verification_email(user.email, user.username) async def on_after_forgot_password(self, user: User, token: str, request: Request | None = None) -> None: # noqa: ARG002 # Request argument is expected in the method signature - logger.info("User %s has forgot their password. Reset token: %s", user.email, token) + logger.info("User %s has forgot their password. Sending reset token", user.email) await send_reset_password_email(user.email, user.username, token) From 8b6f233a99bb7115f1d5fa1ca8fb10350c5a284e Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Wed, 29 Oct 2025 13:28:53 +0100 Subject: [PATCH 17/79] docs(backend): Clarify config setup (local .env file vs. env vars in docker) --- backend/app/api/auth/config.py | 2 +- backend/app/api/plugins/rpi_cam/config.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/app/api/auth/config.py b/backend/app/api/auth/config.py index e412c4f..657e290 100644 --- a/backend/app/api/auth/config.py +++ b/backend/app/api/auth/config.py @@ -29,7 +29,7 @@ class AuthSettings(BaseSettings): email_from: str = "" email_reply_to: str = "" - # Initialize the settings configuration from the .env file + # Initialize the settings configuration from the .env file (or direct environment variables in Docker) model_config = SettingsConfigDict(env_file=BASE_DIR / ".env", extra="ignore") # Set default values for email settings if not provided diff --git a/backend/app/api/plugins/rpi_cam/config.py b/backend/app/api/plugins/rpi_cam/config.py index 4d40723..5088326 100644 --- a/backend/app/api/plugins/rpi_cam/config.py +++ b/backend/app/api/plugins/rpi_cam/config.py @@ -14,7 +14,7 @@ class RPiCamSettings(BaseSettings): # Authentication settings rpi_cam_plugin_secret: str = "" - # Initialize the settings configuration from the .env file + # Initialize the settings configuration from the .env file (or direct environment variables in Docker) model_config = SettingsConfigDict(env_file=BASE_DIR / ".env", extra="ignore") api_key_header_name: str = "X-API-Key" From 2fe6e3699edd1fee312dd6504df73e629f0322e7 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Wed, 29 Oct 2025 13:49:04 +0000 Subject: [PATCH 18/79] feature(frontend-app): Add tooltip to product save button showing validation status --- frontend-app/src/app/products/[id]/index.tsx | 35 ++++++++++++------- .../src/services/api/validation/product.ts | 15 ++++---- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/frontend-app/src/app/products/[id]/index.tsx b/frontend-app/src/app/products/[id]/index.tsx index 3b57952..201b7c1 100644 --- a/frontend-app/src/app/products/[id]/index.tsx +++ b/frontend-app/src/app/products/[id]/index.tsx @@ -1,10 +1,10 @@ import { MaterialCommunityIcons } from '@expo/vector-icons'; import { HeaderBackButton } from '@react-navigation/elements'; import { useLocalSearchParams, useNavigation, useRouter } from 'expo-router'; -import { JSX, useCallback, useEffect, useState } from 'react'; +import { JSX, useCallback, useEffect, useMemo, useState } from 'react'; import { ActivityIndicator, NativeScrollEvent, NativeSyntheticEvent, View } from 'react-native'; import { KeyboardAwareScrollView } from 'react-native-keyboard-controller'; -import { AnimatedFAB, Button, useTheme } from 'react-native-paper'; +import { AnimatedFAB, Button, Tooltip, useTheme } from 'react-native-paper'; import ProductAmountInParent from '@/components/product/ProductAmountInParent'; import ProductComponents from '@/components/product/ProductComponents'; @@ -50,9 +50,13 @@ export default function ProductPage(): JSX.Element { const [editMode, setEditMode] = useState(id === 'new' || false); const [savingState, setSavingState] = useState<'saving' | 'success' | undefined>(undefined); const [fabExtended, setFabExtended] = useState(true); + const [tooltipVisible, setTooltipVisible] = useState(false); const isProductComponent = typeof product.parentID === 'number' && !isNaN(product.parentID); + // Validate product on every change + const validationResult = useMemo(() => validateProduct(product), [product]); + // Callbacks const onProductNameChange = useCallback( (newName: string) => { @@ -256,15 +260,22 @@ export default function ProductPage(): JSX.Element { - + + setTooltipVisible(true)} + style={{ position: 'absolute', right: 0, bottom: 0, overflow: 'hidden', margin: 19 }} + disabled={!validationResult.isValid} + extended={fabExtended} + label={editMode ? 'Save Product' : 'Edit Product'} + visible={product.ownedBy === 'me'} + /> + ); } @@ -312,4 +323,4 @@ function EditNameButton({ }; return ; -} +} \ No newline at end of file diff --git a/frontend-app/src/services/api/validation/product.ts b/frontend-app/src/services/api/validation/product.ts index fd7e7c5..8639dda 100644 --- a/frontend-app/src/services/api/validation/product.ts +++ b/frontend-app/src/services/api/validation/product.ts @@ -95,7 +95,10 @@ export function validateProductVideos(videos: { title: string; url: string }[]): } export function validateProduct(product: Product): ValidationResult { - const { weight, width, height, depth } = product.physicalProperties; + // Handle undefined or incomplete product + if (!product || typeof product !== 'object') { + return { isValid: false, error: 'Invalid product data' }; + } // Validate product name const nameResult = validateProductName(product.name); @@ -103,6 +106,10 @@ export function validateProduct(product: Product): ValidationResult { return nameResult; } + // Safely access physicalProperties with fallback + const physicalProperties = product.physicalProperties || {}; + const { weight, width, height, depth } = physicalProperties; + // Validate weight const weightResult = validateProductWeight(weight); if (!weightResult.isValid) { @@ -132,8 +139,4 @@ export function validateProduct(product: Product): ValidationResult { } return { isValid: true }; -} - -export function isProductValid(product: Product): boolean { - return validateProduct(product).isValid; -} +} \ No newline at end of file From ac2ab05646b7e3ef591b5b4810552110526e7c05 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 3 Nov 2025 11:25:32 +0000 Subject: [PATCH 19/79] feature(backend): add order_by, created_at and updated_at filters for product get endpoint --- backend/app/api/data_collection/filters.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/app/api/data_collection/filters.py b/backend/app/api/data_collection/filters.py index c01d801..4d40d20 100644 --- a/backend/app/api/data_collection/filters.py +++ b/backend/app/api/data_collection/filters.py @@ -58,6 +58,11 @@ class ProductFilter(Filter): dismantling_time_start__lte: datetime | None = None dismantling_time_end__gte: datetime | None = None dismantling_time_end__lte: datetime | None = None + created_at__gte: datetime | None = None + created_at__lte: datetime | None = None + updated_at__gte: datetime | None = None + updated_at__lte: datetime | None = None + order_by: list[str] | None = None search: str | None = None From d0f5247b4ea46287defc7a662fd4c176c0e52a2d Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 4 Nov 2025 16:05:05 +0100 Subject: [PATCH 20/79] feature(backend): Move to mjml email templates and fastapi-email to simplify email setup --- CONTRIBUTING.md | 27 ++ backend/app/api/auth/config.py | 3 +- backend/app/api/auth/utils/email_config.py | 31 +++ .../app/api/auth/utils/programmatic_emails.py | 252 ++++++++---------- backend/app/api/newsletter/routers.py | 16 +- backend/app/api/newsletter/utils/emails.py | 121 ++++----- .../emails/src/components/footer.mjml | 15 ++ .../emails/src/components/header.mjml | 5 + .../emails/src/components/styles.mjml | 9 + .../app/templates/emails/src/newsletter.mjml | 35 +++ .../emails/src/newsletter_subscription.mjml | 28 ++ .../emails/src/newsletter_unsubscribe.mjml | 30 +++ .../templates/emails/src/password_reset.mjml | 25 ++ .../emails/src/post_verification.mjml | 19 ++ .../templates/emails/src/registration.mjml | 50 ++++ .../templates/emails/src/verification.mjml | 25 ++ backend/app/templates/login.html | 4 +- backend/pyproject.toml | 2 + backend/scripts/compile_email_templates.py | 67 +++++ backend/tests/conftest.py | 61 ++++- backend/tests/constants/__init__.py | 1 - backend/tests/constants/background_data.py | 1 - backend/tests/factories/background_data.py | 6 - backend/tests/factories/emails.py | 26 ++ backend/tests/tests/emails/__init__.py | 1 + .../tests/emails/test_programmatic_emails.py | 227 ++++++++++++++++ 26 files changed, 864 insertions(+), 223 deletions(-) create mode 100644 backend/app/api/auth/utils/email_config.py create mode 100644 backend/app/templates/emails/src/components/footer.mjml create mode 100644 backend/app/templates/emails/src/components/header.mjml create mode 100644 backend/app/templates/emails/src/components/styles.mjml create mode 100644 backend/app/templates/emails/src/newsletter.mjml create mode 100644 backend/app/templates/emails/src/newsletter_subscription.mjml create mode 100644 backend/app/templates/emails/src/newsletter_unsubscribe.mjml create mode 100644 backend/app/templates/emails/src/password_reset.mjml create mode 100644 backend/app/templates/emails/src/post_verification.mjml create mode 100644 backend/app/templates/emails/src/registration.mjml create mode 100644 backend/app/templates/emails/src/verification.mjml create mode 100755 backend/scripts/compile_email_templates.py delete mode 100644 backend/tests/constants/__init__.py delete mode 100644 backend/tests/constants/background_data.py delete mode 100644 backend/tests/factories/background_data.py create mode 100644 backend/tests/factories/emails.py create mode 100644 backend/tests/tests/emails/__init__.py create mode 100644 backend/tests/tests/emails/test_programmatic_emails.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e3148c9..b7c8b97 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,6 +20,7 @@ Thank you for your interest in contributing to the Reverse Engineering Lab proje - [Backend Code Style](#backend-code-style) - [Backend Testing](#backend-testing) - [Database Migrations](#database-migrations) + - [Email templates](#email-templates) - [Frontend Development](#frontend-development) - [Frontend Code Style](#frontend-code-style) - [Frontend Testing](#frontend-testing) @@ -339,6 +340,32 @@ When making changes to the database schema: uv run alembic upgrade head ``` +#### Email templates + +This project uses [MJML](https://mjml.io/) to write email templates and [Jinja2](https://jinja.palletsprojects.com/en/latest/) for variable substitution at runtime. + +- **Location** + - Source MJML templates: `backend/app/templates/emails/src/` + - Reusable components: `backend/app/templates/emails/src/components/` + - Compiled HTML output: `backend/app/templates/emails/build/` (This directory is **auto-generated**—do not edit files here.) + +- **Editing Guidelines** + - Use **MJML** for structure and the `{{include:component_name}}` directive to reuse components. + - Use **Jinja2-style variables** in templates, e.g., `{{ username }}`, `{{ verification_link }}`. + - Keep components small and shared styles in `src/components/styles.mjml`. + - **Do not modify** files in `build/`. + +- **Compiling Templates** + Run the compilation script from the repository root: + + ```bash + cd backend + python scripts/compile_email_templates.py + ``` + +- **Interactive Preview** + For visual development, use MJML online tools or the [MJML VS Code extension](https://marketplace.visualstudio.com/items?itemName=mjmlio.vscode-mjml). + ### Frontend Development Set up your environment as described in the [Getting Started](#getting-started) section. diff --git a/backend/app/api/auth/config.py b/backend/app/api/auth/config.py index 657e290..d3ccfb1 100644 --- a/backend/app/api/auth/config.py +++ b/backend/app/api/auth/config.py @@ -2,6 +2,7 @@ from pathlib import Path +from pydantic import SecretStr from pydantic_settings import BaseSettings, SettingsConfigDict # Set the project base directory and .env file @@ -25,7 +26,7 @@ class AuthSettings(BaseSettings): email_host: str = "" email_port: int = 587 # Default SMTP port for TLS email_username: str = "" - email_password: str = "" + email_password: SecretStr = SecretStr("") email_from: str = "" email_reply_to: str = "" diff --git a/backend/app/api/auth/utils/email_config.py b/backend/app/api/auth/utils/email_config.py new file mode 100644 index 0000000..d2d2284 --- /dev/null +++ b/backend/app/api/auth/utils/email_config.py @@ -0,0 +1,31 @@ +"""Email configuration for fastapi-mail. + +This module provides the FastMail instance and configuration for sending emails +throughout the application. +""" + +from pathlib import Path + +from fastapi_mail import ConnectionConfig, FastMail + +from app.api.auth.config import settings as auth_settings + +# Path to pre-compiled HTML email templates +TEMPLATE_FOLDER = Path(__file__).parent.parent.parent.parent / "templates" / "emails" / "build" + +# Configure email connection +email_conf = ConnectionConfig( + MAIL_USERNAME=auth_settings.email_username, + MAIL_PASSWORD=auth_settings.email_password, + MAIL_FROM=auth_settings.email_from, + MAIL_PORT=auth_settings.email_port, + MAIL_SERVER=auth_settings.email_host, + MAIL_STARTTLS=True, + MAIL_SSL_TLS=False, + USE_CREDENTIALS=True, + VALIDATE_CERTS=True, + TEMPLATE_FOLDER=TEMPLATE_FOLDER, +) + +# Create FastMail instance +fm = FastMail(email_conf) diff --git a/backend/app/api/auth/utils/programmatic_emails.py b/backend/app/api/auth/utils/programmatic_emails.py index e9bef95..97bee35 100644 --- a/backend/app/api/auth/utils/programmatic_emails.py +++ b/backend/app/api/auth/utils/programmatic_emails.py @@ -1,168 +1,140 @@ -"""Utilities for sending authentication-related emails.""" +"""Utilities for sending authentication-related emails using fastapi-mail.""" import logging -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from enum import Enum +from typing import Any from urllib.parse import urljoin -import markdown -from aiosmtplib import SMTP, SMTPException +from fastapi import BackgroundTasks +from fastapi_mail import MessageSchema, MessageType +from pydantic import AnyUrl, EmailStr -from app.api.auth.config import settings as auth_settings +from app.api.auth.utils.email_config import fm from app.core.config import settings as core_settings logger: logging.Logger = logging.getLogger(__name__) -### Common email functions ### -# TODO: Move to using MJML or similar templating system for email content. - - -class TextContentType(str, Enum): - """Type for specifying the content type of the email body.""" - - PLAIN = "plain" - HTML = "html" - MARKDOWN = "markdown" - - def body_to_mimetext(self, body: str) -> MIMEText: - """Convert an email body to MIMEText format.""" - match self: - case TextContentType.PLAIN: - return MIMEText(body, "plain") - case TextContentType.HTML: - return MIMEText(body, "html") - case TextContentType.MARKDOWN: - # Convert Markdown to HTML - html = markdown.markdown(body) - return MIMEText(html, "html") +### Helper functions ### +def generate_token_link(token: str, route: str, base_url: str | AnyUrl | None = None) -> str: + """Generate a link with the specified token and route.""" + if base_url is None: + # Default to frontend app URL from core settings + base_url = str(core_settings.frontend_app_url) + return urljoin(str(base_url), f"{route}?token={token}") -async def send_email( - to_email: str, +async def send_email_with_template( + to_email: EmailStr, subject: str, - body: str, - content_type: TextContentType = TextContentType.PLAIN, - headers: dict | None = None, + template_name: str, + template_body: dict[str, Any], + background_tasks: BackgroundTasks | None = None, +) -> None: + """Send an HTML email using a template. + + Args: + to_email: Recipient email address + subject: Email subject line + template_name: Name of the template file (e.g., "registration.html") + template_body: Dictionary of variables to pass to the template + background_tasks: Optional BackgroundTasks instance for async sending + """ + message = MessageSchema( + subject=subject, + recipients=[to_email], + template_body=template_body, + subtype=MessageType.html, + ) + + if background_tasks: + background_tasks.add_task(fm.send_message, message, template_name=template_name) + logger.info("Email queued for background sending to %s using template %s", to_email, template_name) + else: + await fm.send_message(message, template_name=template_name) + logger.info("Email sent to %s using template %s", to_email, template_name) + + +### Authentication email functions ### +async def send_registration_email( + to_email: EmailStr, + username: str | None, + token: str, + background_tasks: BackgroundTasks | None = None, ) -> None: - """Send an email with the specified subject and body.""" - msg = MIMEMultipart() - msg["From"] = auth_settings.email_from - msg["Reply-To"] = auth_settings.email_reply_to - msg["To"] = to_email - msg["Subject"] = subject - - # Add additional headers if provided - if headers: - for key, value in headers.items(): - msg[key] = value - - # Attach the body in the specified content type - msg.attach(content_type.body_to_mimetext(body)) - - try: - # TODO: Investigate use of managed outlook address for sending emails - smtp = SMTP( - hostname=auth_settings.email_host, - port=auth_settings.email_port, - ) - await smtp.connect() - await smtp.login(auth_settings.email_username, auth_settings.email_password) - await smtp.send_message(msg) - await smtp.quit() - logger.info("Email sent to %s", to_email) - except SMTPException as e: - error_message = f"Error sending email: {e}" - raise SMTPException(error_message) from e - - -def generate_token_link(token: str, route: str) -> str: - """Generate a link with the specified token and route.""" - # TODO: Check that the base url works in remote deployment - return urljoin(str(core_settings.frontend_app_url), f"{route}?token={token}") - - -### Email content ### -async def send_registration_email(to_email: str, username: str | None, token: str) -> None: """Send a registration email with verification token.""" - # TODO: Store frontend paths required by the backend in a shared .env or other config file in the root directory - # Alternatively, we can send the right path as a parameter from the frontend to the backend verification_link = generate_token_link(token, "/verify") subject = "Welcome to Reverse Engineering Lab - Verify Your Email" - body = f""" -Hello {username if username else to_email}, - -Thank you for registering! Please verify your email by clicking the link below: -{verification_link} - -This link will expire in 1 hour. - -If you did not register for this service, please ignore this email. - -Best regards, - -The Reverse Engineering Lab Team - """ - - await send_email(subject=subject, body=body, to_email=to_email) - - -async def send_reset_password_email(to_email: str, username: str | None, token: str) -> None: + await send_email_with_template( + to_email=to_email, + subject=subject, + template_name="registration.html", + template_body={ + "username": username if username else to_email, + "verification_link": verification_link, + }, + background_tasks=background_tasks, + ) + + +async def send_reset_password_email( + to_email: EmailStr, + username: str | None, + token: str, + background_tasks: BackgroundTasks | None = None, +) -> None: """Send a reset password email with the token.""" - request_password_link = generate_token_link(token, "/reset-password") + reset_link = generate_token_link(token, "/reset-password") subject = "Password Reset" - body = f""" -Hello {username if username else to_email}, - -Please reset your password by clicking the link below: -{request_password_link} - -This link will expire in 1 hour. - -If you did not request a password reset, please ignore this email. - -Best regards, - -The Reverse Engineering Lab Team - """ - await send_email(to_email, subject, body) - - -async def send_verification_email(to_email: str, username: str | None, token: str) -> None: + await send_email_with_template( + to_email=to_email, + subject=subject, + template_name="password_reset.html", + template_body={ + "username": username if username else to_email, + "reset_link": reset_link, + }, + background_tasks=background_tasks, + ) + + +async def send_verification_email( + to_email: EmailStr, + username: str | None, + token: str, + background_tasks: BackgroundTasks | None = None, +) -> None: """Send a verification email with the token.""" verification_link = generate_token_link(token, "/verify") subject = "Email Verification" - body = f""" -Hello {username if username else to_email}, - -Please verify your email by clicking the link below: - -{verification_link} -This link will expire in 1 hour. - -If you did not request verification, please ignore this email. - -Best regards, - -The Reverse Engineering Lab Team - """ - await send_email(to_email, subject, body) - - -async def send_post_verification_email(to_email: str, username: str | None) -> None: + await send_email_with_template( + to_email=to_email, + subject=subject, + template_name="verification.html", + template_body={ + "username": username if username else to_email, + "verification_link": verification_link, + }, + background_tasks=background_tasks, + ) + + +async def send_post_verification_email( + to_email: EmailStr, + username: str | None, + background_tasks: BackgroundTasks | None = None, +) -> None: """Send a post-verification email.""" subject = "Email Verified" - body = f""" -Hello {username if username else to_email}, - -Your email has been verified! -Best regards, - -The Reverse Engineering Lab Team - """ - await send_email(to_email, subject, body) + await send_email_with_template( + to_email=to_email, + subject=subject, + template_name="post_verification.html", + template_body={ + "username": username if username else to_email, + }, + background_tasks=background_tasks, + ) diff --git a/backend/app/api/newsletter/routers.py b/backend/app/api/newsletter/routers.py index 4483832..83f7b00 100644 --- a/backend/app/api/newsletter/routers.py +++ b/backend/app/api/newsletter/routers.py @@ -3,7 +3,7 @@ from collections.abc import Sequence from typing import Annotated -from fastapi import APIRouter, HTTPException, Security +from fastapi import APIRouter, BackgroundTasks, HTTPException, Security from fastapi.params import Body from pydantic import EmailStr from sqlmodel import select @@ -23,7 +23,9 @@ @backend_router.post("/subscribe", status_code=201, response_model=NewsletterSubscriberRead) -async def subscribe_to_newsletter(email: Annotated[EmailStr, Body()], db: AsyncSessionDep) -> NewsletterSubscriber: +async def subscribe_to_newsletter( + email: Annotated[EmailStr, Body()], db: AsyncSessionDep, background_tasks: BackgroundTasks +) -> NewsletterSubscriber: """Subscribe to the newsletter to receive updates about the app launch.""" # Check if the email already exists existing_subscriber = ( @@ -36,7 +38,7 @@ async def subscribe_to_newsletter(email: Annotated[EmailStr, Body()], db: AsyncS # If not confirmed, generate new token and send email token = create_jwt_token(email, JWTType.NEWSLETTER_CONFIRMATION) - await send_newsletter_subscription_email(email, token) + await send_newsletter_subscription_email(email, token, background_tasks=background_tasks) raise HTTPException( status_code=400, detail="Already subscribed, but not confirmed. A new confirmation email has been sent.", @@ -50,7 +52,7 @@ async def subscribe_to_newsletter(email: Annotated[EmailStr, Body()], db: AsyncS # Send confirmation email token = create_jwt_token(email, JWTType.NEWSLETTER_CONFIRMATION) - await send_newsletter_subscription_email(email, token) + await send_newsletter_subscription_email(email, token, background_tasks=background_tasks) return new_subscriber @@ -83,7 +85,9 @@ async def confirm_newsletter_subscription(token: Annotated[str, Body()], db: Asy @backend_router.post("/request-unsubscribe", status_code=200) -async def request_unsubscribe(email: Annotated[EmailStr, Body()], db: AsyncSessionDep) -> dict: +async def request_unsubscribe( + email: Annotated[EmailStr, Body()], db: AsyncSessionDep, background_tasks: BackgroundTasks +) -> dict: """Request to unsubscribe by sending an email with unsubscribe link.""" # Check if the email is subscribed existing_subscriber = ( @@ -98,7 +102,7 @@ async def request_unsubscribe(email: Annotated[EmailStr, Body()], db: AsyncSessi token = create_jwt_token(email, JWTType.NEWSLETTER_UNSUBSCRIBE) # Send unsubscription email with the link - await send_newsletter_unsubscription_request_email(email, token) + await send_newsletter_unsubscription_request_email(email, token, background_tasks=background_tasks) return {"message": "If you are subscribed, we've sent an unsubscribe link to your email."} diff --git a/backend/app/api/newsletter/utils/emails.py b/backend/app/api/newsletter/utils/emails.py index 32b4459..cc4d85c 100644 --- a/backend/app/api/newsletter/utils/emails.py +++ b/backend/app/api/newsletter/utils/emails.py @@ -1,71 +1,72 @@ """Email sending utilities for the newsletter service.""" -from app.api.auth.utils.programmatic_emails import TextContentType, generate_token_link, send_email +from fastapi import BackgroundTasks +from pydantic import EmailStr + +from app.api.auth.utils.programmatic_emails import generate_token_link, send_email_with_template from app.api.newsletter.utils.tokens import JWTType, create_jwt_token +from app.core.config import settings as core_settings -async def send_newsletter_subscription_email(to_email: str, token: str) -> None: +async def send_newsletter_subscription_email( + to_email: EmailStr, + token: str, + background_tasks: BackgroundTasks | None = None, +) -> None: """Send a newsletter subscription email.""" subject = "Reverse Engineering Lab: Confirm Your Newsletter Subscription" - # TODO: Dynamically generate the confirmation link based on the frontend URL tree - # Alternatively, send the frontend-side link to the backend as a parameter - confirmation_link = generate_token_link(token, "newsletter/confirm") - - body = f""" -Hello, - -Thank you for subscribing to the Reverse Engineering Lab newsletter! - -Please confirm your subscription by clicking [here]({confirmation_link}). - -This link will expire in 24 hours. - -We'll keep you updated with our progress and let you know when the full application is launched. - -Best regards, - -The Reverse Engineering Lab Team - """ - await send_email(to_email, subject, body, content_type=TextContentType.MARKDOWN) - - -async def send_newsletter(to_email: str, subject: str, content: str) -> None: - """Send newsletter with proper unsubscribe headers.""" + confirmation_link = generate_token_link(token, "newsletter/confirm", base_url=core_settings.frontend_web_url) + + await send_email_with_template( + to_email=to_email, + subject=subject, + template_name="newsletter_subscription.html", + template_body={ + "confirmation_link": confirmation_link, + }, + background_tasks=background_tasks, + ) + + +async def send_newsletter( + to_email: EmailStr, + subject: str, + content: str, + background_tasks: BackgroundTasks | None = None, +) -> None: + """Send newsletter with proper unsubscribe link.""" # Create unsubscribe token and link token = create_jwt_token(to_email, JWTType.NEWSLETTER_UNSUBSCRIBE) - unsubscribe_link = generate_token_link(token, "newsletter/unsubscribe") - - # Add footer with unsubscribe link - body = f""" - {content} - ---- -You're receiving this email because you subscribed to the Reverse Engineering Lab newsletter. -To unsubscribe, click [here]({unsubscribe_link}) - """ - - # Add List-Unsubscribe header for email clients that support it - headers = {"List-Unsubscribe": f"<{unsubscribe_link}>", "List-Unsubscribe-Post": "List-Unsubscribe=One-Click"} - - await send_email(to_email, subject, body, content_type=TextContentType.MARKDOWN, headers=headers) - - -async def send_newsletter_unsubscription_request_email(to_email: str, token: str) -> None: + unsubscribe_link = generate_token_link(token, "newsletter/unsubscribe", base_url=core_settings.frontend_web_url) + + await send_email_with_template( + to_email=to_email, + subject=subject, + template_name="newsletter.html", + template_body={ + "subject": subject, + "content": content, + "unsubscribe_link": unsubscribe_link, + }, + background_tasks=background_tasks, + ) + + +async def send_newsletter_unsubscription_request_email( + to_email: EmailStr, + token: str, + background_tasks: BackgroundTasks | None = None, +) -> None: """Send an email with unsubscribe link.""" subject = "Reverse Engineering Lab: Unsubscribe Request" - unsubscribe_link = generate_token_link(token, "newsletter/unsubscribe") - - body = f""" -Hello, - -We received a request to unsubscribe this email address from the Reverse Engineering Lab newsletter. - -If you made this request, please click [here]({unsubscribe_link}) to unsubscribe. - -If you did not request to unsubscribe, you can safely ignore this email. - -Best regards, - -The Reverse Engineering Lab Team - """ - await send_email(to_email, subject, body, content_type=TextContentType.MARKDOWN) + unsubscribe_link = generate_token_link(token, "newsletter/unsubscribe", base_url=core_settings.frontend_web_url) + + await send_email_with_template( + to_email=to_email, + subject=subject, + template_name="newsletter_unsubscribe.html", + template_body={ + "unsubscribe_link": unsubscribe_link, + }, + background_tasks=background_tasks, + ) diff --git a/backend/app/templates/emails/src/components/footer.mjml b/backend/app/templates/emails/src/components/footer.mjml new file mode 100644 index 0000000..19da9ed --- /dev/null +++ b/backend/app/templates/emails/src/components/footer.mjml @@ -0,0 +1,15 @@ + + + + + Best regards,
+ The Reverse Engineering Lab Team +
+
+
+ + + + This email was sent from Reverse Engineering Lab + + diff --git a/backend/app/templates/emails/src/components/header.mjml b/backend/app/templates/emails/src/components/header.mjml new file mode 100644 index 0000000..caafe50 --- /dev/null +++ b/backend/app/templates/emails/src/components/header.mjml @@ -0,0 +1,5 @@ + + + Reverse Engineering Lab + + diff --git a/backend/app/templates/emails/src/components/styles.mjml b/backend/app/templates/emails/src/components/styles.mjml new file mode 100644 index 0000000..a564039 --- /dev/null +++ b/backend/app/templates/emails/src/components/styles.mjml @@ -0,0 +1,9 @@ + + + + + + + .header { font-size: 24px; font-weight: bold; color: #007bff; } .footer { font-size: 12px; color: #666666; } .success + { font-size: 18px; color: #28a745; font-weight: bold; } + diff --git a/backend/app/templates/emails/src/newsletter.mjml b/backend/app/templates/emails/src/newsletter.mjml new file mode 100644 index 0000000..274d6d4 --- /dev/null +++ b/backend/app/templates/emails/src/newsletter.mjml @@ -0,0 +1,35 @@ + + + {{subject}} + {{include:styles}} + + + {{include:header}} + + + + {{content}} + + + + + + + + Best regards,
+ The Reverse Engineering Lab Team +
+
+
+ + + + + + You're receiving this email because you subscribed to the Reverse Engineering Lab newsletter.
+ Unsubscribe +
+
+
+
+
diff --git a/backend/app/templates/emails/src/newsletter_subscription.mjml b/backend/app/templates/emails/src/newsletter_subscription.mjml new file mode 100644 index 0000000..89b0294 --- /dev/null +++ b/backend/app/templates/emails/src/newsletter_subscription.mjml @@ -0,0 +1,28 @@ + + + Reverse Engineering Lab: Confirm Your Newsletter Subscription + {{include:styles}} + + + {{include:header}} + + + + Hello, + Thank you for subscribing to the Reverse Engineering Lab newsletter! + Please confirm your subscription by clicking the button below: + Confirm Subscription + + Or copy and paste this link in your browser:
+ {{confirmation_link}} +
+ This link will expire in 24 hours. + + We'll keep you updated with our progress and let you know when the full application is launched. + +
+
+ + {{include:footer}} +
+
diff --git a/backend/app/templates/emails/src/newsletter_unsubscribe.mjml b/backend/app/templates/emails/src/newsletter_unsubscribe.mjml new file mode 100644 index 0000000..05f2d4b --- /dev/null +++ b/backend/app/templates/emails/src/newsletter_unsubscribe.mjml @@ -0,0 +1,30 @@ + + + Reverse Engineering Lab: Unsubscribe Request + {{include:styles}} + + + + + + {{include:header}} + + + + Hello, + + We received a request to unsubscribe this email address from the Reverse Engineering Lab newsletter. + + If you made this request, please click the button below to unsubscribe: + Unsubscribe + + Or copy and paste this link in your browser:
+ {{unsubscribe_link}} +
+ If you did not request to unsubscribe, you can safely ignore this email. +
+
+ + {{include:footer}} +
+
diff --git a/backend/app/templates/emails/src/password_reset.mjml b/backend/app/templates/emails/src/password_reset.mjml new file mode 100644 index 0000000..0ec11a7 --- /dev/null +++ b/backend/app/templates/emails/src/password_reset.mjml @@ -0,0 +1,25 @@ + + + Password Reset + {{include:styles}} + + + {{include:header}} + + + + Hello {{username}}, + Please reset your password by clicking the button below: + Reset Password + + Or copy and paste this link in your browser:
+ {{reset_link}} +
+ This link will expire in 1 hour. + If you did not request a password reset, please ignore this email. +
+
+ + {{include:footer}} +
+
diff --git a/backend/app/templates/emails/src/post_verification.mjml b/backend/app/templates/emails/src/post_verification.mjml new file mode 100644 index 0000000..8961c8b --- /dev/null +++ b/backend/app/templates/emails/src/post_verification.mjml @@ -0,0 +1,19 @@ + + + Email Verified + {{include:styles}} + + + {{include:header}} + + + + Hello {{username}}, + Your email has been verified! + Thank you for verifying your email address. You can now enjoy full access to all features. + + + + {{include:footer}} + + diff --git a/backend/app/templates/emails/src/registration.mjml b/backend/app/templates/emails/src/registration.mjml new file mode 100644 index 0000000..437203c --- /dev/null +++ b/backend/app/templates/emails/src/registration.mjml @@ -0,0 +1,50 @@ + + + Welcome to Reverse Engineering Lab - Verify Your Email + + + + + + + .header { font-size: 24px; font-weight: bold; color: #007bff; } .footer { font-size: 12px; color: #666666; } + + + + + + Reverse Engineering Lab + + + + + + Hello {{ username }}, + Thank you for registering! Please verify your email by clicking the button below: + Verify Email Address + + Or copy and paste this link in your browser:
+ {{ verification_link }} +
+ This link will expire in 1 hour. + If you did not register for this service, please ignore this email. +
+
+ + + + + + Best regards,
+ The Reverse Engineering Lab Team +
+
+
+ + + + This email was sent from Reverse Engineering Lab + + +
+
diff --git a/backend/app/templates/emails/src/verification.mjml b/backend/app/templates/emails/src/verification.mjml new file mode 100644 index 0000000..cc324a5 --- /dev/null +++ b/backend/app/templates/emails/src/verification.mjml @@ -0,0 +1,25 @@ + + + Email Verification + {{include:styles}} + + + {{include:header}} + + + + Hello {{username}}, + Please verify your email by clicking the button below: + Verify Email Address + + Or copy and paste this link in your browser:
+ {{verification_link}} +
+ This link will expire in 1 hour. + If you did not request verification, please ignore this email. +
+
+ + {{include:footer}} +
+
diff --git a/backend/app/templates/login.html b/backend/app/templates/login.html index 3adfb68..1fb1f6d 100644 --- a/backend/app/templates/login.html +++ b/backend/app/templates/login.html @@ -35,7 +35,7 @@

Login

const errorDiv = document.getElementById('error') const nextInput = document.getElementById('next') const nextValue = nextInput ? nextInput.value : null - + try { const response = await fetch('/auth/cookie/login', { method: 'POST', @@ -49,7 +49,7 @@

Login

}), credentials: 'include' }) - + if (response.ok) { window.location.href = nextValue || '{{ url_for("index") }}' } else { diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 4ed11cf..cd7df75 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -28,6 +28,7 @@ "cachetools>=5.5.2", "email-validator>=2.2.0", "fastapi-filter>=2.0.1", + "fastapi-mail==1.5.2", "fastapi-pagination>=0.13.2", # NOTE: This is a heavy dependency (~40MB) due to its use of boto3, even though we don't use any cloud storage # We should consider using a more lightweight alternative if it becomes available. @@ -38,6 +39,7 @@ "fastapi-users[oauth,sqlalchemy]>=14.0.1", "fastapi[standard] >=0.115.14", "markdown>=3.8.2", + "mjml>=0.11.1", "pillow >=11.2.1", "psycopg[binary] >=3.2.9", # TODO: Upgrade to python 3.14 and pydantic 2.12 when SQLModel fixes compatibility issues (see https://github.com/fastapi/sqlmodel/issues/1606) diff --git a/backend/scripts/compile_email_templates.py b/backend/scripts/compile_email_templates.py new file mode 100755 index 0000000..713e720 --- /dev/null +++ b/backend/scripts/compile_email_templates.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +"""Compile MJML email templates to HTML. + +This script reads MJML templates from app/templates/emails/src/, +expands any {{include:component}} directives from src/components/, +compiles them to HTML, and saves the output to app/templates/emails/build/. +""" + +import logging +from pathlib import Path + +from mjml.mjml2html import mjml_to_html + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# Paths +SCRIPT_DIR = Path(__file__).parent +BACKEND_DIR = SCRIPT_DIR.parent +SRC_DIR = BACKEND_DIR / "app" / "templates" / "emails" / "src" +BUILD_DIR = BACKEND_DIR / "app" / "templates" / "emails" / "build" + + +def compile_mjml_templates() -> None: + """Compile all MJML templates in src/ to HTML in build/.""" + if not SRC_DIR.exists(): + logger.error("Source directory not found: %s", SRC_DIR) + return + + # Create build directory if it doesn't exist + BUILD_DIR.mkdir(parents=True, exist_ok=True) + + # Find all MJML files + mjml_files = list(SRC_DIR.glob("*.mjml")) + + if not mjml_files: + logger.warning("No MJML files found in %s", SRC_DIR) + return + + logger.info("Found %d MJML template(s) to compile", len(mjml_files)) + + # Compile each template + for mjml_file in mjml_files: + try: + logger.info("Compiling %s...", mjml_file.name) + + # Read MJML content + mjml_content = mjml_file.read_text() + + # Compile to HTML + html_dotmap = mjml_to_html(mjml_content) + html_content = html_dotmap.html + + # Write HTML to build directory + html_file = BUILD_DIR / mjml_file.with_suffix(".html").name + html_file.write_text(html_content) + + logger.info(" ✓ Compiled to %s", html_file.name) + + except Exception: + logger.exception(" ✗ Failed to compile %s", mjml_file.name) + + logger.info("Compilation complete!") + + +if __name__ == "__main__": + compile_mjml_templates() diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 0a4732a..5deafde 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -6,12 +6,10 @@ import logging from collections.abc import AsyncGenerator, Generator from pathlib import Path +from unittest.mock import AsyncMock import pytest -from alembic import command from alembic.config import Config -from app.core.config import settings -from app.main import app from fastapi.testclient import TestClient from sqlalchemy import Engine, create_engine, text from sqlalchemy.exc import ProgrammingError @@ -19,6 +17,12 @@ from sqlalchemy.ext.asyncio.engine import AsyncEngine from sqlmodel.ext.asyncio.session import AsyncSession +from alembic import command +from app.core.config import settings +from app.main import app + +from .factories.emails import EmailContextFactory, EmailDataFactory + # Set up logger logger: logging.Logger = logging.getLogger(__name__) @@ -55,7 +59,7 @@ def get_alembic_config() -> Config: return alembic_cfg -@pytest.fixture(scope="session", autouse=True) +@pytest.fixture(scope="session") def setup_test_database() -> Generator: """Create test database, run migrations, and cleanup after tests.""" create_test_database() # Create empty database @@ -72,7 +76,7 @@ def setup_test_database() -> Generator: ### Async test session generators -@pytest.fixture(scope="function") +@pytest.fixture async def get_async_session() -> AsyncGenerator[AsyncSession]: """Create a new database session for each test and roll it back after the test.""" async with async_engine.begin() as connection, async_session_local(bind=connection) as session: @@ -81,7 +85,7 @@ async def get_async_session() -> AsyncGenerator[AsyncSession]: await transaction.rollback() -@pytest.fixture(scope="function") +@pytest.fixture async def client(db: AsyncSession) -> AsyncGenerator[TestClient]: """Provide a TestClient that uses the test database session.""" @@ -94,3 +98,48 @@ async def override_get_db() -> AsyncGenerator[AsyncSession]: yield c app.dependency_overrides.clear() + + +### Email fixtures +@pytest.fixture +def email_context() -> dict: + """Return a realistic email template context dict using FactoryBoy/Faker.""" + return EmailContextFactory() + + +@pytest.fixture +def email_data() -> dict: + """Return realistic test data for email functions using FactoryBoy/Faker.""" + return EmailDataFactory() + + +@pytest.fixture +def mock_smtp() -> AsyncMock: + """Return a configured mock SMTP client for testing email sending.""" + mock = AsyncMock() + mock.connect = AsyncMock() + mock.login = AsyncMock() + mock.send_message = AsyncMock() + mock.quit = AsyncMock() + return mock + + +@pytest.fixture +def mock_email_sender(monkeypatch: pytest.MonkeyPatch) -> AsyncMock: + """Mock the fastapi-mail send_message function for all email tests. + + This fixture automatically patches fm.send_message so tests don't need + to manually patch it with context managers. + + Returns: + AsyncMock: The mocked send_message function + + Usage: + @pytest.mark.asyncio + async def test_send_email(mock_email_sender): + await send_registration_email("test@example.com", "user", "token") + mock_email_sender.assert_called_once() + """ + mock_send = AsyncMock() + monkeypatch.setattr("app.api.auth.utils.programmatic_emails.fm.send_message", mock_send) + return mock_send diff --git a/backend/tests/constants/__init__.py b/backend/tests/constants/__init__.py deleted file mode 100644 index 2564ef9..0000000 --- a/backend/tests/constants/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Constants used in testing.""" diff --git a/backend/tests/constants/background_data.py b/backend/tests/constants/background_data.py deleted file mode 100644 index 196044a..0000000 --- a/backend/tests/constants/background_data.py +++ /dev/null @@ -1 +0,0 @@ -"""Constants for background data tests.""" diff --git a/backend/tests/factories/background_data.py b/backend/tests/factories/background_data.py deleted file mode 100644 index 4ef9990..0000000 --- a/backend/tests/factories/background_data.py +++ /dev/null @@ -1,6 +0,0 @@ -import factory -from app.api.background_data.models import Taxonomy - - -class TaxonomyFactory(factory.alchemy.SQLAlchemyModelFactory): - pass diff --git a/backend/tests/factories/emails.py b/backend/tests/factories/emails.py new file mode 100644 index 0000000..1974be0 --- /dev/null +++ b/backend/tests/factories/emails.py @@ -0,0 +1,26 @@ +"""Factories for email template context dicts for tests.""" + +from factory.base import DictFactory +from factory.faker import Faker + + +class EmailContextFactory(DictFactory): + """Produce realistic email template context dicts for tests.""" + + username = Faker("user_name") + verification_link = Faker("url") + reset_link = Faker("url") + confirmation_link = Faker("url") + unsubscribe_link = Faker("url") + subject = Faker("sentence", nb_words=5) + newsletter_content = Faker("text", max_nb_chars=200) + + +class EmailDataFactory(DictFactory): + """Produce test data for email sending functions.""" + + email = Faker("email") + username = Faker("user_name") + token = Faker("uuid4") + subject = Faker("sentence", nb_words=5) + body = Faker("text", max_nb_chars=200) diff --git a/backend/tests/tests/emails/__init__.py b/backend/tests/tests/emails/__init__.py new file mode 100644 index 0000000..d04ba40 --- /dev/null +++ b/backend/tests/tests/emails/__init__.py @@ -0,0 +1 @@ +"""Tests related to email functionality.""" diff --git a/backend/tests/tests/emails/test_programmatic_emails.py b/backend/tests/tests/emails/test_programmatic_emails.py new file mode 100644 index 0000000..390a857 --- /dev/null +++ b/backend/tests/tests/emails/test_programmatic_emails.py @@ -0,0 +1,227 @@ +"""Tests for programmatic email sending functionality.""" + +from typing import Any +from unittest.mock import AsyncMock, MagicMock +from urllib.parse import parse_qs, urlparse + +import pytest +from faker import Faker +from fastapi import BackgroundTasks + +from app.api.auth.utils.programmatic_emails import ( + generate_token_link, + send_post_verification_email, + send_registration_email, + send_reset_password_email, + send_verification_email, +) +from app.core.config import settings as core_settings + +fake = Faker() + + +### Token Link Generation Tests ### +def test_generate_token_link_default_base_url() -> None: + """Test token link generation with default base URL from core settings.""" + token = fake.uuid4() + route = "/verify" + + link = generate_token_link(token, route) + + parsed = urlparse(link) + query_params = parse_qs(parsed.query) + + assert link.startswith(str(core_settings.frontend_app_url)) + assert parsed.path == route + assert query_params["token"] == [token] + + +def test_generate_token_link_custom_base_url() -> None: + """Test token link generation with custom base URL.""" + token = fake.uuid4() + route = "/reset-password" + custom_base_url = fake.url() + + link = generate_token_link(token, route, base_url=custom_base_url) + + parsed = urlparse(link) + query_params = parse_qs(parsed.query) + + assert link.startswith(custom_base_url) + assert parsed.path == route + assert query_params["token"] == [token] + + +def test_generate_token_link_with_trailing_slash() -> None: + """Test that token links are generated correctly regardless of trailing slashes.""" + token = fake.uuid4() + route = "/verify" + base_url_with_slash = f"{fake.url()}//" + + link = generate_token_link(token, route, base_url=base_url_with_slash) + + # Should not have double slashes + assert "//" not in link.replace("https://", "") # noqa: PLR2004 # Magic value for double slash + # Should still have the correct route + assert urlparse(link).path == route + + +### Registration Email Tests ### +@pytest.mark.asyncio +async def test_send_registration_email(email_data: dict, mock_email_sender: AsyncMock) -> None: + """Test registration email is sent.""" + await send_registration_email(email_data["email"], email_data["username"], email_data["token"]) + mock_email_sender.assert_called_once() + + +@pytest.mark.asyncio +async def test_send_registration_email_no_username(email_data: dict, mock_email_sender: AsyncMock) -> None: + """Test registration email works without username.""" + await send_registration_email(email_data["email"], None, email_data["token"]) + mock_email_sender.assert_called_once() + + +@pytest.mark.asyncio +async def test_send_registration_email_with_background_tasks(email_data: dict, mock_email_sender: AsyncMock) -> None: + """Test registration email queues task instead of sending immediately.""" + background_tasks = MagicMock(spec=BackgroundTasks) + + await send_registration_email( + email_data["email"], email_data["username"], email_data["token"], background_tasks=background_tasks + ) + + # When background_tasks is provided, it should queue, not send + background_tasks.add_task.assert_called_once() + mock_email_sender.assert_not_called() + + +### Password Reset Email Tests ### +@pytest.mark.asyncio +async def test_send_reset_password_email(email_data: dict, mock_email_sender: AsyncMock) -> None: + """Test password reset email is sent.""" + await send_reset_password_email(email_data["email"], email_data["username"], email_data["token"]) + mock_email_sender.assert_called_once() + + +@pytest.mark.asyncio +async def test_send_reset_password_email_with_background_tasks(email_data: dict, mock_email_sender: AsyncMock) -> None: + """Test password reset email queues task when background_tasks provided.""" + background_tasks = MagicMock(spec=BackgroundTasks) + + await send_reset_password_email( + email_data["email"], email_data["username"], email_data["token"], background_tasks=background_tasks + ) + + background_tasks.add_task.assert_called_once() + mock_email_sender.assert_not_called() + + +### Verification Email Tests ### +@pytest.mark.asyncio +async def test_send_verification_email(email_data: dict, mock_email_sender: AsyncMock) -> None: + """Test verification email is sent.""" + await send_verification_email(email_data["email"], email_data["username"], email_data["token"]) + mock_email_sender.assert_called_once() + + +@pytest.mark.asyncio +async def test_send_verification_email_with_background_tasks(email_data: dict, mock_email_sender: AsyncMock) -> None: + """Test verification email queues task when background_tasks provided.""" + background_tasks = MagicMock(spec=BackgroundTasks) + + await send_verification_email( + email_data["email"], email_data["username"], email_data["token"], background_tasks=background_tasks + ) + + background_tasks.add_task.assert_called_once() + mock_email_sender.assert_not_called() + + +### Post-Verification Email Tests ### +@pytest.mark.asyncio +async def test_send_post_verification_email(email_data: dict, mock_email_sender: AsyncMock) -> None: + """Test post-verification email is sent.""" + await send_post_verification_email(email_data["email"], email_data["username"]) + mock_email_sender.assert_called_once() + + +@pytest.mark.asyncio +async def test_send_post_verification_email_no_username(email_data: dict, mock_email_sender: AsyncMock) -> None: + """Test post-verification email works without username.""" + await send_post_verification_email(email_data["email"], None) + mock_email_sender.assert_called_once() + + +@pytest.mark.asyncio +async def test_send_post_verification_email_with_background_tasks( + email_data: dict, mock_email_sender: AsyncMock +) -> None: + """Test post-verification email queues task when background_tasks provided.""" + background_tasks = MagicMock(spec=BackgroundTasks) + + await send_post_verification_email(email_data["email"], email_data["username"], background_tasks=background_tasks) + + background_tasks.add_task.assert_called_once() + mock_email_sender.assert_not_called() + + +### Parametrized Integration Tests ### +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("email_func", "needs_token"), + [ + (send_registration_email, True), + (send_reset_password_email, True), + (send_verification_email, True), + (send_post_verification_email, False), + ], +) +async def test_all_email_functions_send_emails( + email_data: dict, + mock_email_sender: AsyncMock, + email_func: Any, + *, + needs_token: bool, +) -> None: + """Test that all email functions successfully send emails.""" + # Call function with appropriate arguments + if needs_token: + await email_func(email_data["email"], email_data["username"], email_data["token"]) + else: + await email_func(email_data["email"], email_data["username"]) + + # Verify email was sent + mock_email_sender.assert_called_once() + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("email_func", "needs_token"), + [ + (send_registration_email, True), + (send_reset_password_email, True), + (send_verification_email, True), + (send_post_verification_email, False), + ], +) +async def test_all_email_functions_support_background_tasks( + email_data: dict, + mock_email_sender: AsyncMock, + email_func: Any, + *, + needs_token: bool, +) -> None: + """Test that all email functions support background tasks.""" + background_tasks = MagicMock(spec=BackgroundTasks) + + # Call function with background tasks + if needs_token: + await email_func( + email_data["email"], email_data["username"], email_data["token"], background_tasks=background_tasks + ) + else: + await email_func(email_data["email"], email_data["username"], background_tasks=background_tasks) + + # Verify task was queued, not sent immediately + background_tasks.add_task.assert_called_once() + mock_email_sender.assert_not_called() From ebfa5ae824f9a4ebd29f74cbb94ccf00f142d815 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 6 Nov 2025 16:07:29 +0100 Subject: [PATCH 21/79] feature(backend): Move to fastapi-mail and redis for disposable mail cache. We can use the Redis cache for faster session management as well. --- .pre-commit-config.yaml | 100 +++++----- backend/.env.example | 6 + backend/app/api/admin/auth.py | 2 +- backend/app/api/auth/config.py | 12 +- backend/app/api/auth/crud/users.py | 8 +- backend/app/api/auth/routers/auth.py | 15 +- backend/app/api/auth/routers/oauth.py | 4 +- backend/app/api/auth/services/user_manager.py | 12 +- .../app/api/auth/utils/email_validation.py | 175 +++++++++++++----- backend/app/api/newsletter/utils/tokens.py | 6 +- backend/app/core/config.py | 6 + backend/app/core/redis.py | 129 +++++++++++++ backend/app/main.py | 59 +++++- backend/pyproject.toml | 4 +- backend/uv.lock | 106 ++++++++++- compose.override.yml | 4 + compose.prod.yml | 4 + compose.yml | 14 +- 18 files changed, 530 insertions(+), 136 deletions(-) create mode 100644 backend/app/core/redis.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ea48998..42a0b64 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,85 +5,85 @@ repos: ### Global hooks - - repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update - rev: v0.8.0 +- repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update + rev: v0.9.0 hooks: - - id: pre-commit-update # Autoupdate pre-commit hooks + - id: pre-commit-update # Autoupdate pre-commit hooks # TODO: Re-add mdformat to pre-commit-update when mdformat plugins are compatible with mdformat 1.0.0 args: [--exclude, mdformat] - - repo: https://github.com/gitleaks/gitleaks - rev: v8.28.0 +- repo: https://github.com/gitleaks/gitleaks + rev: v8.29.0 hooks: - - id: gitleaks + - id: gitleaks - - repo: https://github.com/executablebooks/mdformat +- repo: https://github.com/executablebooks/mdformat rev: 0.7.22 hooks: - - id: mdformat # Format Markdown files. + - id: mdformat # Format Markdown files. additional_dependencies: - - mdformat-gfm # Support GitHub Flavored Markdown. - - mdformat-footnote - - mdformat-frontmatter - - mdformat-ruff # Support Python code blocks linted with Ruff. + - mdformat-gfm # Support GitHub Flavored Markdown. + - mdformat-footnote + - mdformat-frontmatter + - mdformat-ruff # Support Python code blocks linted with Ruff. - - repo: https://github.com/pre-commit/pre-commit-hooks +- repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - - id: check-added-large-files - - id: check-case-conflict # Check for files with names that differ only in case. - - id: check-executables-have-shebangs - - id: check-shebang-scripts-are-executable - - id: check-toml - - id: check-yaml + - id: check-added-large-files + - id: check-case-conflict # Check for files with names that differ only in case. + - id: check-executables-have-shebangs + - id: check-shebang-scripts-are-executable + - id: check-toml + - id: check-yaml exclude: ^docs/mkdocs.yml$ # Exclude mkdocs.yml because it uses an obscure tag to allow for mermaid formatting - - id: detect-private-key - - id: end-of-file-fixer # Ensure files end with a newline. - - id: mixed-line-ending - - id: no-commit-to-branch # Prevent commits to main and master branches. - - id: trailing-whitespace + - id: detect-private-key + - id: end-of-file-fixer # Ensure files end with a newline. + - id: mixed-line-ending + - id: no-commit-to-branch # Prevent commits to main and master branches. + - id: trailing-whitespace args: ["--markdown-linebreak-ext", "md"] # Preserve Markdown hard line breaks. - - repo: https://github.com/commitizen-tools/commitizen +- repo: https://github.com/commitizen-tools/commitizen rev: v4.9.1 hooks: - - id: commitizen + - id: commitizen stages: [commit-msg] - - repo: https://github.com/simonvanlierde/check-json5 +- repo: https://github.com/simonvanlierde/check-json5 rev: v1.1.0 hooks: - - id: check-json5 + - id: check-json5 files: ^ (?!(backend/frontend-app|frontend-web)/data/) - ### Backend hooks - - repo: https://github.com/RobertCraigie/pyright-python # Lint backend code with Pyright. + ### Backend hooks +- repo: https://github.com/RobertCraigie/pyright-python # Lint backend code with Pyright. rev: v1.1.407 hooks: - - id: pyright + - id: pyright files: ^backend/(app|scripts|tests)/ entry: pyright --project backend - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.2 +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.14.3 hooks: - - id: ruff-check # Lint code + - id: ruff-check # Lint code files: ^backend/(app|scripts|tests)/ args: ["--fix", "--config", "backend/pyproject.toml", "--ignore", "FIX002"] # Allow TODO comments in commits. - - id: ruff-format # Format code + - id: ruff-format # Format code files: ^backend/(app|scripts|tests)/ args: ["--config", "backend/pyproject.toml"] - - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.5 +- repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.9.7 hooks: - - id: uv-lock # Update the uv lockfile for the backend. + - id: uv-lock # Update the uv lockfile for the backend. files: ^backend/(uv\.lock|pyproject\.toml|uv\.toml)$ entry: uv lock --project backend - - repo: local +- repo: local hooks: # Check if Alembic migrations are up-to-date. Uses uv to ensure the right environment when executed through VS Code Git extension. - - id: backend-alembic-autogen-check + - id: backend-alembic-autogen-check name: check alembic migrations entry: bash -c 'cd backend && uv run alembic-autogen-check' language: system @@ -91,22 +91,22 @@ repos: pass_filenames: false stages: [pre-commit] - ### Frontend hooks - - repo: local + ### Frontend hooks +- repo: local hooks: - - id: frontend-web-format + - id: frontend-web-format name: format frontend-web code entry: bash -c 'cd frontend-web && npm run format' - language: - system + language: system # Match frontend JavaScript and TypeScript files for formatting. - files: ^frontend-web\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ + files: + ^frontend-web\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ pass_filenames: false - - id: frontend-app-format + - id: frontend-app-format name: format frontend-app code entry: bash -c 'cd frontend-app && npm run format' - language: - system + language: system # Match frontend JavaScript and TypeScript files for formatting. - files: ^frontend-app\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ + files: + ^frontend-app\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ pass_filenames: false diff --git a/backend/.env.example b/backend/.env.example index ab1001c..fac58ca 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -28,6 +28,12 @@ EMAIL_PASSWORD='your-email-password' # 🔀 Password for EMAIL_FROM='Your Name ' # 🔀 Email address from which the emails are sent. Can be different from the SMTP server username. EMAIL_REPLY_TO='your.replyto.alias.@example.com' # 🔀 Email address to which replies are sent. Can be different from the SMTP server username. +# Redis settings for caching (disposable email domains, sessions, etc.) +REDIS_HOST='localhost' # 🔀 Redis server host (use 'cache' in Docker) +REDIS_PORT='6379' # 🔀 Redis server port +REDIS_DB='0' # 🔀 Redis database number (0-15) +REDIS_PASSWORD='password' # 🔀 Redis password (leave empty if no password) + # Superuser details SUPERUSER_EMAIL='your-email@example.com' # 🔀 SUPERUSER_PASSWORD='example_password' # 🔀 diff --git a/backend/app/api/admin/auth.py b/backend/app/api/admin/auth.py index 9371707..f3916af 100644 --- a/backend/app/api/admin/auth.py +++ b/backend/app/api/admin/auth.py @@ -64,7 +64,7 @@ async def authenticate(self, request: Request) -> RedirectResponse | Response | def get_authentication_backend() -> AdminAuth: """Get the authentication backend for the SQLAdmin interface.""" - return AdminAuth(secret_key=auth_settings.fastapi_users_secret) + return AdminAuth(secret_key=auth_settings.fastapi_users_secret.get_secret_value()) async def logout_override(request: Request) -> RedirectResponse: # noqa: ARG001 # Signature expected by the SQLAdmin implementation diff --git a/backend/app/api/auth/config.py b/backend/app/api/auth/config.py index d3ccfb1..5a519b4 100644 --- a/backend/app/api/auth/config.py +++ b/backend/app/api/auth/config.py @@ -13,14 +13,14 @@ class AuthSettings(BaseSettings): """Settings class to store settings related to auth components.""" # Authentication settings - fastapi_users_secret: str = "" - newsletter_secret: str = "" + fastapi_users_secret: SecretStr = SecretStr("") + newsletter_secret: SecretStr = SecretStr("") # OAuth settings - google_oauth_client_id: str = "" - google_oauth_client_secret: str = "" - github_oauth_client_id: str = "" - github_oauth_client_secret: str = "" + google_oauth_client_id: SecretStr = SecretStr("") + google_oauth_client_secret: SecretStr = SecretStr("") + github_oauth_client_id: SecretStr = SecretStr("") + github_oauth_client_secret: SecretStr = SecretStr("") # Settings used to configure the email server for sending emails from the app. email_host: str = "" diff --git a/backend/app/api/auth/crud/users.py b/backend/app/api/auth/crud/users.py index d2906e5..2942899 100644 --- a/backend/app/api/auth/crud/users.py +++ b/backend/app/api/auth/crud/users.py @@ -14,13 +14,15 @@ UserCreateWithOrganization, UserUpdate, ) -from app.api.auth.utils.email_validation import is_disposable_email +from app.api.auth.utils.email_validation import EmailChecker from app.api.common.crud.utils import db_get_model_with_id_if_it_exists ## Create User ## async def create_user_override( - user_db: BaseUserDatabase[User, UUID4], user_create: UserCreate | UserCreateWithOrganization + user_db: BaseUserDatabase[User, UUID4], + user_create: UserCreate | UserCreateWithOrganization, + email_checker: EmailChecker | None = None, ) -> UserCreate: """Override of base user creation with additional username uniqueness check. @@ -28,7 +30,7 @@ async def create_user_override( """ # TODO: Fix type errors in this method and implement custom UserNameAlreadyExists error in FastAPI-Users - if await is_disposable_email(user_create.email): + if email_checker and await email_checker.is_disposable(user_create.email): raise DisposableEmailError(email=user_create.email) if user_create.username is not None: diff --git a/backend/app/api/auth/routers/auth.py b/backend/app/api/auth/routers/auth.py index 22a105a..d63266a 100644 --- a/backend/app/api/auth/routers/auth.py +++ b/backend/app/api/auth/routers/auth.py @@ -1,11 +1,13 @@ """Authentication, registration, and login routes.""" -from fastapi import APIRouter +from typing import Annotated + +from fastapi import APIRouter, Depends from pydantic import EmailStr from app.api.auth.schemas import UserCreate, UserCreateWithOrganization, UserRead from app.api.auth.services.user_manager import bearer_auth_backend, cookie_auth_backend, fastapi_user_manager -from app.api.auth.utils.email_validation import is_disposable_email +from app.api.auth.utils.email_validation import EmailChecker, get_email_checker_dependency from app.api.common.routers.openapi import mark_router_routes_public router = APIRouter(prefix="/auth", tags=["auth"]) @@ -38,8 +40,13 @@ @router.get("/validate-email") -async def validate_email(email: EmailStr) -> dict: +async def validate_email( + email: EmailStr, + email_checker: Annotated[EmailChecker | None, Depends(get_email_checker_dependency)], +) -> dict: """Validate email address for registration.""" - is_disposable = await is_disposable_email(email) + is_disposable = False + if email_checker: + is_disposable = await email_checker.is_disposable(email) return {"isValid": not is_disposable, "reason": "Please use a permanent email address" if is_disposable else None} diff --git a/backend/app/api/auth/routers/oauth.py b/backend/app/api/auth/routers/oauth.py index eba947c..51e5478 100644 --- a/backend/app/api/auth/routers/oauth.py +++ b/backend/app/api/auth/routers/oauth.py @@ -32,7 +32,7 @@ fastapi_user_manager.get_oauth_router( oauth_client, auth_backend, - settings.fastapi_users_secret, + settings.fastapi_users_secret.get_secret_value(), associate_by_email=True, is_verified_by_default=True, ), @@ -44,7 +44,7 @@ fastapi_user_manager.get_oauth_associate_router( oauth_client, UserRead, - settings.fastapi_users_secret, + settings.fastapi_users_secret.get_secret_value(), ), prefix=f"/{provider_name}/associate", ) diff --git a/backend/app/api/auth/services/user_manager.py b/backend/app/api/auth/services/user_manager.py index 0eedec0..c8a312c 100644 --- a/backend/app/api/auth/services/user_manager.py +++ b/backend/app/api/auth/services/user_manager.py @@ -35,7 +35,7 @@ logger = logging.getLogger(__name__) # Declare constants -SECRET: str = auth_settings.fastapi_users_secret +SECRET: SecretStr = auth_settings.fastapi_users_secret ACCESS_TOKEN_TTL = auth_settings.access_token_ttl_seconds RESET_TOKEN_TTL = auth_settings.reset_password_token_ttl_seconds VERIFICATION_TOKEN_TTL = auth_settings.verification_token_ttl_seconds @@ -45,10 +45,10 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, UUID4]): """User manager class for FastAPI-Users.""" # Set up token secrets and lifetimes - reset_password_token_secret: SecretType = SECRET + reset_password_token_secret: SecretType = SECRET.get_secret_value() reset_password_token_lifetime_seconds = RESET_TOKEN_TTL - verification_token_secret: SecretType = SECRET + verification_token_secret: SecretType = SECRET.get_secret_value() verification_token_lifetime_seconds = VERIFICATION_TOKEN_TTL async def create( @@ -59,7 +59,9 @@ async def create( ) -> User: """Override of base user creation with additional username uniqueness check and organization creation.""" try: - user_create = await create_user_override(self.user_db, user_create) + # Get email checker from app state if request is available + email_checker = request.app.state.email_checker if request else None + user_create = await create_user_override(self.user_db, user_create, email_checker) # HACK: This is a temporary solution to allow error propagation for username and organization creation errors. # The built-in UserManager register route can only catch UserAlreadyExists and InvalidPasswordException errors. # TODO: Implement custom exceptions in custom register router, this will also simplify user creation crud. @@ -172,7 +174,7 @@ async def get_user_manager(user_db: SQLModelUserDatabaseAsync = Depends(get_user def get_jwt_strategy() -> JWTStrategy: """Get a JWT strategy to be used in authentication backends.""" - return JWTStrategy(secret=SECRET, lifetime_seconds=ACCESS_TOKEN_TTL) + return JWTStrategy(secret=SECRET.get_secret_value(), lifetime_seconds=ACCESS_TOKEN_TTL) # Authentication backends diff --git a/backend/app/api/auth/utils/email_validation.py b/backend/app/api/auth/utils/email_validation.py index 36dfefa..ac2c6de 100644 --- a/backend/app/api/auth/utils/email_validation.py +++ b/backend/app/api/auth/utils/email_validation.py @@ -1,55 +1,130 @@ """Utilities for validating email addresses.""" -from datetime import UTC, datetime, timedelta -from pathlib import Path +import asyncio +import contextlib +import logging -import anyio -import httpx +from fastapi import Request +from fastapi_mail.email_utils import DefaultChecker +from redis.asyncio import Redis +from redis.exceptions import RedisError +logger = logging.getLogger(__name__) + +# Custom source for disposable domains DISPOSABLE_DOMAINS_URL = "https://raw.githubusercontent.com/disposable/disposable-email-domains/master/domains.txt" -BASE_DIR: Path = (Path(__file__).parents[4]).resolve() - -CACHE_FILE = BASE_DIR / "data" / "cache" / "disposable_domains_cache.txt" -CACHE_DURATION = timedelta(days=1) - - -async def get_disposable_domains() -> set[str]: - """Get disposable email domains, using cache if fresh.""" - # Check if cache exists and is fresh - if CACHE_FILE.exists(): - cache_age = datetime.now(tz=UTC) - datetime.fromtimestamp(CACHE_FILE.stat().st_mtime, tz=UTC) - if cache_age < CACHE_DURATION: - async with await anyio.open_file(CACHE_FILE, "r") as f: - content = await f.read() - return {line.strip().lower() for line in content.splitlines() if line.strip()} - - # Fetch fresh list - try: - async with httpx.AsyncClient() as client: - response = await client.get(DISPOSABLE_DOMAINS_URL, timeout=10.0) - response.raise_for_status() - domains = {line.strip().lower() for line in response.text.splitlines() if line.strip()} - - # Ensure cache directory exists - CACHE_FILE.parent.mkdir(parents=True, exist_ok=True) - - # Update cache - async with await anyio.open_file(CACHE_FILE, "w") as f: - await f.write("\n".join(sorted(domains))) - - return domains - except (httpx.RequestError, httpx.HTTPStatusError, OSError): - # If fetch fails and cache exists, use stale cache - if CACHE_FILE.exists(): - async with await anyio.open_file(CACHE_FILE, "r") as f: - content = await f.read() - return {line.strip().lower() for line in content.splitlines() if line.strip()} - # If no cache available, return empty set (allow registration) - return set() - - -async def is_disposable_email(email: str) -> bool: - """Check if email domain is disposable.""" - domain = email.split("@")[-1].lower() - disposable_domains = await get_disposable_domains() - return domain in disposable_domains + + +class EmailChecker: + """Email checker that manages disposable domain validation.""" + + def __init__(self, redis_client: Redis) -> None: + """Initialize email checker with Redis client. + + Args: + redis_client: Redis client instance to use for caching + """ + self.redis_client = redis_client + self.checker: DefaultChecker | None = None + self._refresh_task: asyncio.Task | None = None + + async def initialize(self) -> None: + """Initialize the disposable email checker. + + Should be called during application startup. + """ + try: + self.checker = DefaultChecker(db_provider="redis", source=DISPOSABLE_DOMAINS_URL) + await self.checker.init_redis() + logger.info("Disposable email checker initialized successfully") + + # Fetch initial domains + await self._refresh_domains() + + # Start periodic refresh task + self._refresh_task = asyncio.create_task(self._periodic_refresh()) + + except (RuntimeError, ValueError, ConnectionError, OSError, RedisError) as e: + logger.warning("Failed to initialize disposable email checker: %s", e) + self.checker = None + + async def _refresh_domains(self) -> None: + """Refresh the list of disposable email domains from the source.""" + if self.checker is None: + logger.warning("Email checker not initialized, cannot refresh domains") + return + try: + await self.checker.fetch_temp_email_domains() + logger.info("Disposable email domains refreshed successfully") + except (RuntimeError, ValueError, ConnectionError, OSError, RedisError): + logger.exception("Failed to refresh disposable email domains:") + + async def _periodic_refresh(self) -> None: + """Periodically refresh disposable domains every 24 hours.""" + while True: + try: + await asyncio.sleep(60 * 60 * 24) # 24 hours + await self._refresh_domains() + except asyncio.CancelledError: + logger.info("Periodic domain refresh task cancelled") + break + except (RuntimeError, ValueError, ConnectionError, OSError, RedisError): + logger.exception("Error in periodic domain refresh:") + + async def close(self) -> None: + """Close the email checker and cleanup resources. + + Should be called during application shutdown. + """ + # Cancel periodic refresh task + if self._refresh_task is not None: + self._refresh_task.cancel() + with contextlib.suppress(asyncio.CancelledError): + await self._refresh_task + + # Close checker connections if initialized + if self.checker is not None: + try: + await self.checker.close_connections() + logger.info("Email checker closed successfully") + except (RuntimeError, ValueError, ConnectionError, OSError, RedisError) as e: + logger.warning("Error closing email checker: %s", e) + finally: + self.checker = None + + async def is_disposable(self, email: str) -> bool: + """Check if email domain is disposable. + + Args: + email: Email address to check + + Returns: + bool: True if email is from a disposable domain, False otherwise + """ + if self.checker is None: + logger.warning("Email checker not initialized, allowing registration") + return False + try: + return await self.checker.is_disposable(email) + except (RuntimeError, ValueError, ConnectionError, OSError, RedisError): + logger.exception("Failed to check if email is disposable: %s. Allowing registration.", email) + # If check fails, allow registration (fail open) + return False + + +def get_email_checker_dependency(request: Request) -> EmailChecker | None: + """FastAPI dependency to get EmailChecker from app state. + + Args: + request: FastAPI request object + + Returns: + EmailChecker instance or None if not initialized + + Usage: + @app.get("/example") + async def example(email_checker: EmailChecker | None = Depends(get_email_checker_dependency)): + if email_checker: + await email_checker.is_disposable("test@example.com") + """ + return request.app.state.email_checker diff --git a/backend/app/api/newsletter/utils/tokens.py b/backend/app/api/newsletter/utils/tokens.py index 04ba19c..f94bdf5 100644 --- a/backend/app/api/newsletter/utils/tokens.py +++ b/backend/app/api/newsletter/utils/tokens.py @@ -4,10 +4,12 @@ from enum import Enum import jwt +from pydantic import SecretStr from app.api.auth.config import settings ALGORITHM = "HS256" # Algorithm used for JWT encoding/decoding +SECRET: SecretStr = settings.newsletter_secret class JWTType(str, Enum): @@ -33,13 +35,13 @@ def create_jwt_token(email: str, token_type: JWTType) -> str: """Create a JWT token for newsletter confirmation.""" expiration = datetime.now(UTC) + timedelta(seconds=token_type.expiration_seconds) payload = {"sub": email, "exp": expiration, "type": token_type.value} - return jwt.encode(payload, settings.newsletter_secret, algorithm=ALGORITHM) + return jwt.encode(payload, SECRET.get_secret_value(), algorithm=ALGORITHM) def verify_jwt_token(token: str, expected_token_type: JWTType) -> str | None: """Verify the JWT token and return the email if valid.""" try: - payload = jwt.decode(token, settings.newsletter_secret, algorithms=[ALGORITHM]) + payload = jwt.decode(token, SECRET.get_secret_value(), algorithms=[ALGORITHM]) if payload["type"] != expected_token_type.value: return None return payload["sub"] # Returns the email address from the token diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 7bf2740..9aa8888 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -21,6 +21,12 @@ class CoreSettings(BaseSettings): postgres_db: str = "relab_db" postgres_test_db: str = "relab_test_db" + # Redis settings for caching + redis_host: str = "localhost" + redis_port: int = 6379 + redis_db: int = 0 + redis_password: str = "" + # Debug settings debug: bool = False diff --git a/backend/app/core/redis.py b/backend/app/core/redis.py new file mode 100644 index 0000000..282bf72 --- /dev/null +++ b/backend/app/core/redis.py @@ -0,0 +1,129 @@ +"""Redis connection management.""" + +import logging +from typing import Any + +from fastapi import Request +from redis.asyncio import Redis +from redis.exceptions import RedisError + +from app.core.config import settings + +logger = logging.getLogger(__name__) + + +async def init_redis() -> Redis: + """Initialize Redis client instance with connection pooling. + + Returns: + Redis: Async Redis client with connection pooling + + This should be called once during application startup. + """ + redis_client = Redis( + host=settings.redis_host, + port=settings.redis_port, + db=settings.redis_db, + password=settings.redis_password if settings.redis_password else None, + decode_responses=True, + socket_connect_timeout=5, + socket_timeout=5, + ) + + # Verify connection on startup + try: + await redis_client.pubsub().ping() + logger.info("Redis client initialized and connected: %s:%s", settings.redis_host, settings.redis_port) + except (TimeoutError, RedisError, OSError): + logger.exception("Failed to connect to Redis during initialization.") + raise + + return redis_client + + +async def close_redis(redis_client: Redis) -> None: + """Close Redis connection and connection pool. + + Args: + redis_client: Redis client to close + + This properly closes all connections in the pool. + """ + if redis_client: + await redis_client.aclose() + logger.info("Redis connection pool closed") + + +async def ping_redis(redis_client: Redis) -> bool: + """Check if Redis is available (health check). + + Args: + redis_client: Redis client to ping + + Returns: + bool: True if Redis is responding, False otherwise + + This is useful for health check endpoints. + """ + try: + await redis_client.pubsub().ping() + except (TimeoutError, RedisError, OSError) as e: + logger.warning("Redis ping failed: %s", e) + return False + else: + return True + + +async def get_redis_value(redis_client: Redis, key: str) -> str | None: + """Get value from Redis. + + Args: + redis_client: Redis client + key: Redis key + + Returns: + Value as string, or None if not found + """ + try: + return await redis_client.get(key) + except (TimeoutError, RedisError, OSError): + logger.exception("Failed to get Redis value for key %s.", key) + return None + + +async def set_redis_value(redis_client: Redis, key: str, value: Any, ex: int | None = None) -> bool: + """Set value in Redis. + + Args: + redis_client: Redis client + key: Redis key + value: Value to stores + ex: Expiration time in seconds (optional) + + Returns: + bool: True if successful, False otherwise + """ + try: + await redis_client.set(key, value, ex=ex) + except (TimeoutError, RedisError, OSError): + logger.exception("Failed to set Redis value for key %s.", key) + return False + else: + return True + + +def get_redis_dependency(request: Request) -> Redis: + """FastAPI dependency to get Redis client from app state. + + Args: + request: FastAPI request object + + Returns: + Redis: Redis client instance + + Usage: + @app.get("/example") + async def example(redis: Redis = Depends(get_redis_dependency)): + await redis.get("key") + """ + return request.app.state.redis diff --git a/backend/app/main.py b/backend/app/main.py index 8623cfe..2104b08 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -4,27 +4,84 @@ mounts static and upload directories, and initializes the admin interface. """ +import logging +from collections.abc import AsyncGenerator +from contextlib import asynccontextmanager + from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from fastapi_pagination import add_pagination from app.api.admin.main import init_admin +from app.api.auth.utils.email_validation import EmailChecker from app.api.common.routers.exceptions import register_exception_handlers from app.api.common.routers.main import router from app.api.common.routers.openapi import init_openapi_docs from app.core.config import settings from app.core.database import async_engine +from app.core.redis import close_redis, init_redis from app.core.utils.custom_logging import setup_logging # Initialize logging setup_logging() +logger = logging.getLogger(__name__) + + +@asynccontextmanager +async def lifespan(app: FastAPI) -> AsyncGenerator: + """Manage application lifespan: startup and shutdown events.""" + # Startup + logger.info("Starting up application...") + + # Initialize Redis connection and store in app.state + # The init_redis() function will verify the connection on startup + try: + app.state.redis = await init_redis() + except (ConnectionError, OSError) as e: + logger.warning("Failed to initialize Redis: %s", e) + app.state.redis = None + + # Initialize disposable email checker and store in app.state + app.state.email_checker = None + if app.state.redis is not None: + try: + email_checker = EmailChecker(app.state.redis) + await email_checker.initialize() + app.state.email_checker = email_checker + except (RuntimeError, ValueError, ConnectionError) as e: + logger.warning("Failed to initialize email checker: %s", e) + + logger.info("Application startup complete") + + yield + + # Shutdown + logger.info("Shutting down application...") + + # Close email checker (this will cancel background tasks) + if app.state.email_checker is not None: + try: + await app.state.email_checker.close() + except (RuntimeError, OSError) as e: + logger.warning("Error closing email checker: %s", e) + + # Close Redis connection + if app.state.redis is not None: + try: + await close_redis(app.state.redis) + except (ConnectionError, OSError) as e: + logger.warning("Error closing Redis: %s", e) + + logger.info("Application shutdown complete") + -# Initialize FastAPI application +# Initialize FastAPI application with lifespan app = FastAPI( openapi_url=None, docs_url=None, redoc_url=None, + lifespan=lifespan, ) # Add CORS middleware diff --git a/backend/pyproject.toml b/backend/pyproject.toml index cd7df75..fd4f4c1 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -21,8 +21,7 @@ readme = "README.md" ## Dependencies and version constraints - dependencies = [ # Core dependencies. - "aiosmtplib>=4.0.1", + dependencies = [ "asyncache>=0.3.1", "asyncpg>=0.30.0", "cachetools>=5.5.2", @@ -48,6 +47,7 @@ "pydantic-settings >=2.10.1", "python-dotenv >=1.1.1", "python-slugify>=8.0.4", + "redis>=5.2.1", "relab-rpi-cam-models>=0.1.1", "sqlalchemy >=2.0.41", "sqlmodel >=0.0.24", diff --git a/backend/uv.lock b/backend/uv.lock index 99661a9..44bede3 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -8,11 +8,11 @@ resolution-markers = [ [[package]] name = "aiosmtplib" -version = "5.0.0" +version = "3.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/15/c2dc93a58d716bce64b53918d3cf667d86c96a56a9f3a239a9f104643637/aiosmtplib-5.0.0.tar.gz", hash = "sha256:514ac11c31cb767c764077eb3c2eb2ae48df6f63f1e847aeb36119c4fc42b52d", size = 61057, upload-time = "2025-10-19T19:12:31.426Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/2a/812517f8350cd317aad2ba1ce25dfc213c6f1f2e62e1cbf662b4bdc51d34/aiosmtplib-3.0.2.tar.gz", hash = "sha256:08fd840f9dbc23258025dca229e8a8f04d2ccf3ecb1319585615bfc7933f7f47", size = 59941, upload-time = "2024-07-31T05:13:10.065Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/42/b997c306dc54e6ac62a251787f6b5ec730797eea08e0336d8f0d7b899d5f/aiosmtplib-5.0.0-py3-none-any.whl", hash = "sha256:95eb0f81189780845363ab0627e7f130bca2d0060d46cd3eeb459f066eb7df32", size = 27048, upload-time = "2025-10-19T19:12:30.124Z" }, + { url = "https://files.pythonhosted.org/packages/87/35/441faea7a11159795881a6ec869454f40269e4e3806dced935a35d83a412/aiosmtplib-3.0.2-py3-none-any.whl", hash = "sha256:8783059603a34834c7c90ca51103c3aa129d5922003b5ce98dbaa6d4440f10fc", size = 27111, upload-time = "2024-07-31T05:13:08.515Z" }, ] [[package]] @@ -198,6 +198,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a9/cf/45fb5261ece3e6b9817d3d82b2f343a505fd58674a92577923bc500bd1aa/bcrypt-4.3.0-cp39-abi3-win_amd64.whl", hash = "sha256:e53e074b120f2877a35cc6c736b8eb161377caae8925c17688bd46ba56daaa5b", size = 152799, upload-time = "2025-02-28T01:23:53.139Z" }, ] +[[package]] +name = "beautifulsoup4" +version = "4.14.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822, upload-time = "2025-09-29T10:05:42.613Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392, upload-time = "2025-09-29T10:05:43.771Z" }, +] + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + [[package]] name = "boto3" version = "1.40.55" @@ -489,6 +511,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] +[[package]] +name = "docopt" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } + +[[package]] +name = "dotmap" +version = "1.3.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/68/c186606e4f2bf731abd18044ea201e70c3c244bf468f41368820d197fca5/dotmap-1.3.30.tar.gz", hash = "sha256:5821a7933f075fb47563417c0e92e0b7c031158b4c9a6a7e56163479b658b368", size = 12391, upload-time = "2022-04-06T16:26:49.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/f9/976d6813c160d6c89196d81e9466dca1503d20e609d8751f3536daf37ec6/dotmap-1.3.30-py3-none-any.whl", hash = "sha256:bd9fa15286ea2ad899a4d1dc2445ed85a1ae884a42effb87c89a6ecce71243c6", size = 11464, upload-time = "2022-04-06T16:26:47.103Z" }, +] + [[package]] name = "email-validator" version = "2.2.0" @@ -610,6 +647,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/88/afc022ad64d12f730141fc50758ecf9d60de5fed11335dc16e3127617f05/fastapi_filter-2.0.1-py3-none-any.whl", hash = "sha256:711d48707ec62f7c9e12a7713fc0f6a99858a9e3741b4d108102d5599e77197d", size = 11586, upload-time = "2024-12-07T17:30:05.375Z" }, ] +[[package]] +name = "fastapi-mail" +version = "1.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiosmtplib" }, + { name = "blinker" }, + { name = "email-validator" }, + { name = "jinja2" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/65/0c/05837963c44ce15e4c81e95bb8bb8a2910fddd60a2f41ac5c015c068c53e/fastapi_mail-1.5.2.tar.gz", hash = "sha256:c83b96f1a030db754e83c64d8687b62b3d4f847d25b5adb00f30d5765ff9825a", size = 13312, upload-time = "2025-10-16T11:13:40.484Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/da/30e1a709d85a3132220538714af09b9befad9e81703021dde698c884187a/fastapi_mail-1.5.2-py3-none-any.whl", hash = "sha256:158ecf49075430cb6a5483f557a8f45b987e31f2105a77b3239933b0bacb03e5", size = 15153, upload-time = "2025-10-16T11:13:39.529Z" }, +] + [[package]] name = "fastapi-pagination" version = "0.14.3" @@ -778,6 +833,8 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" }, { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" }, { url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210, upload-time = "2025-08-07T13:18:24.072Z" }, + { url = "https://files.pythonhosted.org/packages/1c/53/f9c440463b3057485b8594d7a638bed53ba531165ef0ca0e6c364b5cc807/greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b", size = 1564759, upload-time = "2025-11-04T12:42:19.395Z" }, + { url = "https://files.pythonhosted.org/packages/47/e4/3bb4240abdd0a8d23f4f88adec746a3099f0d86bfedb623f063b2e3b4df0/greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929", size = 1634288, upload-time = "2025-11-04T12:42:21.174Z" }, { url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685, upload-time = "2025-08-07T13:24:38.824Z" }, { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" }, { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" }, @@ -785,6 +842,8 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" }, { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" }, { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" }, + { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508, upload-time = "2025-11-04T12:42:23.427Z" }, + { url = "https://files.pythonhosted.org/packages/0d/da/343cd760ab2f92bac1845ca07ee3faea9fe52bee65f7bcb19f16ad7de08b/greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681", size = 1680760, upload-time = "2025-11-04T12:42:25.341Z" }, { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425, upload-time = "2025-08-07T13:32:27.59Z" }, ] @@ -1034,6 +1093,21 @@ 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 = "mjml" +version = "0.11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "docopt" }, + { name = "dotmap" }, + { name = "jinja2" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/68/4e0e1b0bc64f0d3afac2fb8a4fb35f2a4e9a0521ae1c777c0e29e21b27fa/mjml-0.11.1.tar.gz", hash = "sha256:f703c8b3458ca0100df6cf56a3633f193b352a80b1a1836a452b92361e74ca73", size = 66589, upload-time = "2025-05-13T10:24:05.693Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/a6/7ed27888adbf8cbdd734e298691004918ec0ef5f40e6bc1329ed97da2273/mjml-0.11.1-py3-none-any.whl", hash = "sha256:fef9f7a95929cbe5ddce9351ee8702e05153d68abc77dcf8e84da2c22a330b2a", size = 63191, upload-time = "2025-05-13T10:24:03.953Z" }, +] + [[package]] name = "nodeenv" version = "1.9.1" @@ -1637,23 +1711,33 @@ wheels = [ { 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 = "redis" +version = "7.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/8f/f125feec0b958e8d22c8f0b492b30b1991d9499a4315dfde466cf4289edc/redis-7.0.1.tar.gz", hash = "sha256:c949df947dca995dc68fdf5a7863950bf6df24f8d6022394585acc98e81624f1", size = 4755322, upload-time = "2025-10-27T14:34:00.33Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/97/9f22a33c475cda519f20aba6babb340fb2f2254a02fb947816960d1e669a/redis-7.0.1-py3-none-any.whl", hash = "sha256:4977af3c7d67f8f0eb8b6fec0dafc9605db9343142f634041fb0235f67c0588a", size = 339938, upload-time = "2025-10-27T14:33:58.553Z" }, +] + [[package]] name = "relab-backend" version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "aiosmtplib" }, { name = "asyncache" }, { name = "asyncpg" }, { name = "cachetools" }, { name = "email-validator" }, { name = "fastapi", extra = ["standard"] }, { name = "fastapi-filter" }, + { name = "fastapi-mail" }, { name = "fastapi-pagination" }, { name = "fastapi-storages" }, { name = "fastapi-users", extra = ["oauth", "sqlalchemy"] }, { name = "fastapi-users-db-sqlmodel" }, { name = "markdown" }, + { name = "mjml" }, { name = "pillow" }, { name = "psycopg", extra = ["binary"] }, { name = "pydantic" }, @@ -1661,6 +1745,7 @@ dependencies = [ { name = "pydantic-settings" }, { name = "python-dotenv" }, { name = "python-slugify" }, + { name = "redis" }, { name = "relab-rpi-cam-models" }, { name = "sqlalchemy" }, { name = "sqlmodel" }, @@ -1698,18 +1783,19 @@ tests = [ [package.metadata] requires-dist = [ - { name = "aiosmtplib", specifier = ">=4.0.1" }, { name = "asyncache", specifier = ">=0.3.1" }, { name = "asyncpg", specifier = ">=0.30.0" }, { name = "cachetools", specifier = ">=5.5.2" }, { name = "email-validator", specifier = ">=2.2.0" }, { name = "fastapi", extras = ["standard"], specifier = ">=0.115.14" }, { name = "fastapi-filter", specifier = ">=2.0.1" }, + { name = "fastapi-mail", specifier = "==1.5.2" }, { name = "fastapi-pagination", specifier = ">=0.13.2" }, { name = "fastapi-storages", specifier = ">=0.3.0" }, { name = "fastapi-users", extras = ["oauth", "sqlalchemy"], specifier = ">=14.0.1" }, { name = "fastapi-users-db-sqlmodel", git = "https://github.com/simonvanlierde/fastapi-users-db-sqlmodel?rev=7e9c4830e53ee20c38e3de80066cb19d7c3efc43" }, { name = "markdown", specifier = ">=3.8.2" }, + { name = "mjml", specifier = ">=0.11.1" }, { name = "pillow", specifier = ">=11.2.1" }, { name = "psycopg", extras = ["binary"], specifier = ">=3.2.9" }, { name = "pydantic", specifier = ">=2.11,<2.12" }, @@ -1717,6 +1803,7 @@ requires-dist = [ { name = "pydantic-settings", specifier = ">=2.10.1" }, { name = "python-dotenv", specifier = ">=1.1.1" }, { name = "python-slugify", specifier = ">=8.0.4" }, + { name = "redis", specifier = ">=5.2.1" }, { name = "relab-rpi-cam-models", specifier = ">=0.1.1" }, { name = "sqlalchemy", specifier = ">=2.0.41" }, { name = "sqlmodel", specifier = ">=0.0.24" }, @@ -1947,6 +2034,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] +[[package]] +name = "soupsieve" +version = "2.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" }, +] + [[package]] name = "sqladmin" version = "0.21.0" diff --git a/compose.override.yml b/compose.override.yml index e6acd8b..d1b6eaf 100644 --- a/compose.override.yml +++ b/compose.override.yml @@ -15,6 +15,10 @@ services: - /opt/relab/backend/.venv # Prevent overwriting local virtual environment - /opt/relab/backend/logs # Prevent overwriting local logs + cache: + ports: + - "6379:6379" + database: ports: - "5433:5432" diff --git a/compose.prod.yml b/compose.prod.yml index dc1c5a3..82908a0 100644 --- a/compose.prod.yml +++ b/compose.prod.yml @@ -31,6 +31,10 @@ services: - user_uploads:/data/uploads:ro - ${BACKUP_DIR:-./backend/backups}/user_uploads:/backups + cache: + volumes: # Persist cache data in production + - cache_data:/data + cloudflared: # Cloudflared tunnel to cml-relab.org image: cloudflare/cloudflared:latest@sha256:89ee50efb1e9cb2ae30281a8a404fed95eb8f02f0a972617526f8c5b417acae2 command: tunnel --no-autoupdate run diff --git a/compose.yml b/compose.yml index 1d194ae..792db55 100644 --- a/compose.yml +++ b/compose.yml @@ -21,6 +21,8 @@ services: depends_on: database: condition: service_healthy + cache: + condition: service_healthy env_file: ./backend/.env environment: DATABASE_HOST: database @@ -35,15 +37,17 @@ services: image: postgres:18@sha256:41bfa2e5b168fff0847a5286694a86cff102bdc4d59719869f6b117bb30b3a24 env_file: ./backend/.env healthcheck: - test: # Check if the database is ready to accept connections - ["CMD-SHELL", "pg_isready -h localhost -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] - interval: 5s - timeout: 5s - retries: 5 + test: ["CMD-SHELL", "pg_isready -h localhost -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] restart: unless-stopped volumes: - database_data:/var/lib/postgresql + cache: + image: redis:8 + healthcheck: + test: ["CMD-SHELL", "redis-cli ping"] + restart: unless-stopped + docs: image: squidfunk/mkdocs-material:9@sha256:980e11fed03b8e7851e579be9f34b1210f516c9f0b4da1a1457f21a460bd6628 restart: unless-stopped From 31bd113ba11d3112f306e5099a2f3d0c638d8e02 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 6 Nov 2025 18:49:19 +0100 Subject: [PATCH 22/79] feature(backend): add order_by query param to get_products route --- backend/app/api/common/crud/base.py | 2 ++ backend/app/api/data_collection/routers.py | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/app/api/common/crud/base.py b/backend/app/api/common/crud/base.py index 2864aaa..f183630 100644 --- a/backend/app/api/common/crud/base.py +++ b/backend/app/api/common/crud/base.py @@ -95,6 +95,8 @@ def get_models_query( statement = add_filter_joins(statement, model, model_filter) # Apply the filter statement = model_filter.filter(statement) + # Apply sorting - fastapi-filter stores it but doesn't apply it automatically + statement = model_filter.sort(statement) relationships_to_exclude = [] statement, relationships_to_exclude = add_relationship_options( diff --git a/backend/app/api/data_collection/routers.py b/backend/app/api/data_collection/routers.py index c70f2dd..cd4ebbb 100644 --- a/backend/app/api/data_collection/routers.py +++ b/backend/app/api/data_collection/routers.py @@ -228,9 +228,6 @@ async def get_products( else: statement: SelectOfScalar[Product] = select(Product).where(Product.parent_id == None) - if product_filter: - statement = product_filter.filter(statement) - return await get_paginated_models( session, Product, From 662c32e24a40400c4551f394794383013f279dd5 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 6 Nov 2025 18:50:26 +0100 Subject: [PATCH 23/79] fix(backend): graceful handling of failed redis connection on app startup --- backend/app/core/redis.py | 43 +++++++++++++++++++++------------------ backend/app/main.py | 8 ++------ 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/backend/app/core/redis.py b/backend/app/core/redis.py index 282bf72..af91b16 100644 --- a/backend/app/core/redis.py +++ b/backend/app/core/redis.py @@ -12,33 +12,34 @@ logger = logging.getLogger(__name__) -async def init_redis() -> Redis: +async def init_redis() -> Redis | None: """Initialize Redis client instance with connection pooling. Returns: - Redis: Async Redis client with connection pooling + Redis: Async Redis client with connection pooling, or None if connection fails This should be called once during application startup. + Gracefully handles connection failures and returns None if Redis is unavailable. """ - redis_client = Redis( - host=settings.redis_host, - port=settings.redis_port, - db=settings.redis_db, - password=settings.redis_password if settings.redis_password else None, - decode_responses=True, - socket_connect_timeout=5, - socket_timeout=5, - ) - - # Verify connection on startup try: + redis_client = Redis( + host=settings.redis_host, + port=settings.redis_port, + db=settings.redis_db, + password=settings.redis_password if settings.redis_password else None, + decode_responses=True, + socket_connect_timeout=5, + socket_timeout=5, + ) + + # Verify connection on startup await redis_client.pubsub().ping() logger.info("Redis client initialized and connected: %s:%s", settings.redis_host, settings.redis_port) - except (TimeoutError, RedisError, OSError): - logger.exception("Failed to connect to Redis during initialization.") - raise + return redis_client - return redis_client + except (TimeoutError, RedisError, OSError, ConnectionError) as e: + logger.warning("Failed to connect to Redis during initialization: %s. Application will continue without Redis.", e) + return None async def close_redis(redis_client: Redis) -> None: @@ -112,18 +113,20 @@ async def set_redis_value(redis_client: Redis, key: str, value: Any, ex: int | N return True -def get_redis_dependency(request: Request) -> Redis: +def get_redis_dependency(request: Request) -> Redis | None: """FastAPI dependency to get Redis client from app state. Args: request: FastAPI request object Returns: - Redis: Redis client instance + Redis client instance, or None if Redis is not available Usage: @app.get("/example") - async def example(redis: Redis = Depends(get_redis_dependency)): + async def example(redis: Redis | None = Depends(get_redis_dependency)): + if redis is None: + raise HTTPException(status_code=503, detail="Redis is not available") await redis.get("key") """ return request.app.state.redis diff --git a/backend/app/main.py b/backend/app/main.py index 2104b08..7d0757f 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -35,12 +35,8 @@ async def lifespan(app: FastAPI) -> AsyncGenerator: logger.info("Starting up application...") # Initialize Redis connection and store in app.state - # The init_redis() function will verify the connection on startup - try: - app.state.redis = await init_redis() - except (ConnectionError, OSError) as e: - logger.warning("Failed to initialize Redis: %s", e) - app.state.redis = None + # The init_redis() function will verify the connection on startup and return None if it fails + app.state.redis = await init_redis() # Initialize disposable email checker and store in app.state app.state.email_checker = None From 9db5f411d3d28ac42748622dede9423a2181e092 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Fri, 7 Nov 2025 12:41:36 +0100 Subject: [PATCH 24/79] fix(backend): Include built email templates in git --- backend/.dockerignore | 3 + backend/.gitignore | 3 + .../templates/emails/build/newsletter.html | 259 ++++++++++++++ .../emails/build/newsletter_subscription.html | 170 +++++++++ .../emails/build/newsletter_unsubscribe.html | 165 +++++++++ .../emails/build/password_reset.html | 165 +++++++++ .../emails/build/post_verification.html | 141 ++++++++ .../templates/emails/build/registration.html | 324 ++++++++++++++++++ .../templates/emails/build/verification.html | 165 +++++++++ 9 files changed, 1395 insertions(+) create mode 100644 backend/app/templates/emails/build/newsletter.html create mode 100644 backend/app/templates/emails/build/newsletter_subscription.html create mode 100644 backend/app/templates/emails/build/newsletter_unsubscribe.html create mode 100644 backend/app/templates/emails/build/password_reset.html create mode 100644 backend/app/templates/emails/build/post_verification.html create mode 100644 backend/app/templates/emails/build/registration.html create mode 100644 backend/app/templates/emails/build/verification.html diff --git a/backend/.dockerignore b/backend/.dockerignore index 4c510a3..2c42c71 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -96,3 +96,6 @@ MANIFEST # Local logs ./logs + +# Include built email templates +!app/templates/emails/build/ \ No newline at end of file diff --git a/backend/.gitignore b/backend/.gitignore index 38f0b26..966df64 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -80,3 +80,6 @@ backups/* # VS Code settings !.vscode/settings.json !.vscode/extensions.json + +# Include built email templates +!app/templates/emails/build/ \ No newline at end of file diff --git a/backend/app/templates/emails/build/newsletter.html b/backend/app/templates/emails/build/newsletter.html new file mode 100644 index 0000000..5a11a13 --- /dev/null +++ b/backend/app/templates/emails/build/newsletter.html @@ -0,0 +1,259 @@ + + + + {{ subject }} + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+
{{ content }}
+
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + + + + + + +
+

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

+ + +
+
+ +
+
+ +
+ + diff --git a/backend/app/templates/emails/build/newsletter_subscription.html b/backend/app/templates/emails/build/newsletter_subscription.html new file mode 100644 index 0000000..74db9ce --- /dev/null +++ b/backend/app/templates/emails/build/newsletter_subscription.html @@ -0,0 +1,170 @@ + + + + Reverse Engineering Lab: Confirm Your Newsletter Subscription + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
Hello,
+
+
Thank you for subscribing to the Reverse Engineering Lab newsletter!
+
+
Please confirm your subscription by clicking the button below:
+
+ + + + +
+ Confirm Subscription +
+
+
+ Or copy and paste this link in your browser:
+ {{ confirmation_link }} +
+
+
This link will expire in 24 hours.
+
+
We'll keep you updated with our progress and let you know when the full application is launched.
+
+
+ +
+
+ +
+ + diff --git a/backend/app/templates/emails/build/newsletter_unsubscribe.html b/backend/app/templates/emails/build/newsletter_unsubscribe.html new file mode 100644 index 0000000..2bba8b1 --- /dev/null +++ b/backend/app/templates/emails/build/newsletter_unsubscribe.html @@ -0,0 +1,165 @@ + + + + Reverse Engineering Lab: Unsubscribe Request + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+
Hello,
+
+
We received a request to unsubscribe this email address from the Reverse Engineering Lab newsletter.
+
+
If you made this request, please click the button below to unsubscribe:
+
+ + + + +
+ Unsubscribe +
+
+
+ Or copy and paste this link in your browser:
+ {{ unsubscribe_link }} +
+
+
If you did not request to unsubscribe, you can safely ignore this email.
+
+
+ +
+
+ +
+ + diff --git a/backend/app/templates/emails/build/password_reset.html b/backend/app/templates/emails/build/password_reset.html new file mode 100644 index 0000000..0974956 --- /dev/null +++ b/backend/app/templates/emails/build/password_reset.html @@ -0,0 +1,165 @@ + + + + Password Reset + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+
Hello {{ username }},
+
+
Please reset your password by clicking the button below:
+
+ + + + +
+ Reset Password +
+
+
+ Or copy and paste this link in your browser:
+ {{ reset_link }} +
+
+
This link will expire in 1 hour.
+
+
If you did not request a password reset, please ignore this email.
+
+
+ +
+
+ +
+ + diff --git a/backend/app/templates/emails/build/post_verification.html b/backend/app/templates/emails/build/post_verification.html new file mode 100644 index 0000000..39f7850 --- /dev/null +++ b/backend/app/templates/emails/build/post_verification.html @@ -0,0 +1,141 @@ + + + + Email Verified + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + +
+
Hello {{ username }},
+
+
Your email has been verified!
+
+
Thank you for verifying your email address. You can now enjoy full access to all features.
+
+
+ +
+
+ +
+ + diff --git a/backend/app/templates/emails/build/registration.html b/backend/app/templates/emails/build/registration.html new file mode 100644 index 0000000..a37667d --- /dev/null +++ b/backend/app/templates/emails/build/registration.html @@ -0,0 +1,324 @@ + + + + Welcome to Reverse Engineering Lab - Verify Your Email + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+
Reverse Engineering Lab
+
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+
Hello {{ username }},
+
+
Thank you for registering! Please verify your email by clicking the button below:
+
+ + + + +
+ Verify Email Address +
+
+
+ Or copy and paste this link in your browser:
+ {{ verification_link }} +
+
+
This link will expire in 1 hour.
+
+
If you did not register for this service, please ignore this email.
+
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + + + + + + +
+

+ + +
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + + + +
+
+ +
+
+ +
+ + diff --git a/backend/app/templates/emails/build/verification.html b/backend/app/templates/emails/build/verification.html new file mode 100644 index 0000000..d71be70 --- /dev/null +++ b/backend/app/templates/emails/build/verification.html @@ -0,0 +1,165 @@ + + + + Email Verification + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+
Hello {{ username }},
+
+
Please verify your email by clicking the button below:
+
+ + + + +
+ Verify Email Address +
+
+
+ Or copy and paste this link in your browser:
+ {{ verification_link }} +
+
+
This link will expire in 1 hour.
+
+
If you did not request verification, please ignore this email.
+
+
+ +
+
+ +
+ + From 5c4d92c306a5b0393de72711b9d63fcc9921fcde Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Fri, 7 Nov 2025 13:56:38 +0100 Subject: [PATCH 25/79] fix(backend): Fix cache dependency in docker compose file (backend, not backend-migrations, depends on cache) --- compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compose.yml b/compose.yml index 792db55..1994d98 100644 --- a/compose.yml +++ b/compose.yml @@ -6,6 +6,8 @@ services: depends_on: database: condition: service_healthy + cache: + condition: service_healthy env_file: ./backend/.env environment: DATABASE_HOST: database # Point to the database service @@ -21,8 +23,6 @@ services: depends_on: database: condition: service_healthy - cache: - condition: service_healthy env_file: ./backend/.env environment: DATABASE_HOST: database From 775728b3e702483854633dd729ce540fc95554fd Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Fri, 7 Nov 2025 16:48:51 +0000 Subject: [PATCH 26/79] chore: update pre-commit revs --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 42a0b64..6b9ca26 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -65,7 +65,7 @@ repos: entry: pyright --project backend - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.3 + rev: v0.14.4 hooks: - id: ruff-check # Lint code files: ^backend/(app|scripts|tests)/ From 36434c3c987d55c0623cfac7016b47283473a796 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Fri, 7 Nov 2025 16:49:43 +0000 Subject: [PATCH 27/79] feature(backend): Use SecretStr for secret env vars in the core config --- backend/app/core/config.py | 10 +++++----- backend/app/core/redis.py | 2 +- backend/scripts/create_superuser.py | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 9aa8888..04313c1 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -3,7 +3,7 @@ from functools import cached_property from pathlib import Path -from pydantic import EmailStr, HttpUrl, PostgresDsn, computed_field +from pydantic import EmailStr, HttpUrl, PostgresDsn, SecretStr, computed_field from pydantic_settings import BaseSettings, SettingsConfigDict # Set the project base directory and .env file @@ -17,7 +17,7 @@ class CoreSettings(BaseSettings): database_host: str = "localhost" database_port: int = 5432 postgres_user: str = "postgres" - postgres_password: str = "" + postgres_password: SecretStr = SecretStr("") postgres_db: str = "relab_db" postgres_test_db: str = "relab_test_db" @@ -25,14 +25,14 @@ class CoreSettings(BaseSettings): redis_host: str = "localhost" redis_port: int = 6379 redis_db: int = 0 - redis_password: str = "" + redis_password: SecretStr = SecretStr("") # Debug settings debug: bool = False # Superuser settings superuser_email: EmailStr = "your-email@example.com" - superuser_password: str = "" + superuser_password: SecretStr = SecretStr("") # Network settings frontend_web_url: HttpUrl = HttpUrl("http://127.0.0.1:8000") @@ -55,7 +55,7 @@ class CoreSettings(BaseSettings): def _build_database_url(self, driver: str, database: str) -> str: """Build and validate PostgreSQL database URL.""" url = ( - f"postgresql+{driver}://{self.postgres_user}:{self.postgres_password}" + f"postgresql+{driver}://{self.postgres_user}:{self.postgres_password.get_secret_value()}" f"@{self.database_host}:{self.database_port}/{database}" ) PostgresDsn(url) # Validate URL format diff --git a/backend/app/core/redis.py b/backend/app/core/redis.py index af91b16..31e5302 100644 --- a/backend/app/core/redis.py +++ b/backend/app/core/redis.py @@ -26,7 +26,7 @@ async def init_redis() -> Redis | None: host=settings.redis_host, port=settings.redis_port, db=settings.redis_db, - password=settings.redis_password if settings.redis_password else None, + password=settings.redis_password.get_secret_value() if settings.redis_password else None, decode_responses=True, socket_connect_timeout=5, socket_timeout=5, diff --git a/backend/scripts/create_superuser.py b/backend/scripts/create_superuser.py index 1b044fe..c70b78a 100755 --- a/backend/scripts/create_superuser.py +++ b/backend/scripts/create_superuser.py @@ -6,11 +6,12 @@ import logging import anyio +from fastapi_users.exceptions import InvalidPasswordException, UserAlreadyExists + from app.api.auth.schemas import UserCreate from app.api.auth.utils.programmatic_user_crud import create_user from app.core.config import settings from app.core.database import get_async_session -from fastapi_users.exceptions import InvalidPasswordException, UserAlreadyExists # Set up logging logger: logging.Logger = logging.getLogger(__name__) @@ -35,7 +36,7 @@ async def create_superuser() -> None: async_session=async_session, user_create=UserCreate( email=superuser_email, - password=superuser_password, + password=superuser_password.get_secret_value(), organization_id=None, is_superuser=True, is_verified=True, From fd86e95055e38f37ab491cee775010f165f38b1d Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Fri, 7 Nov 2025 16:52:39 +0000 Subject: [PATCH 28/79] fix(docker): Improve healthcheck timing and override REDIS_HOST var in backend services --- compose.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compose.yml b/compose.yml index 1994d98..8937ad6 100644 --- a/compose.yml +++ b/compose.yml @@ -11,6 +11,7 @@ services: env_file: ./backend/.env environment: DATABASE_HOST: database # Point to the database service + REDIS_HOST: cache # Point to the cache service restart: unless-stopped volumes: - user_uploads:/opt/relab/backend/data/uploads @@ -26,6 +27,7 @@ services: env_file: ./backend/.env environment: DATABASE_HOST: database + REDIS_HOST: cache profiles: - migrations restart: on-failure:3 @@ -38,14 +40,20 @@ services: env_file: ./backend/.env healthcheck: test: ["CMD-SHELL", "pg_isready -h localhost -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + start_period: 5s + start_interval: 5s restart: unless-stopped volumes: - database_data:/var/lib/postgresql cache: image: redis:8 + command: ["sh", "-c", "redis-server --appendonly yes --save 60 1 --requirepass $${REDIS_PASSWORD}"] + env_file: ./backend/.env healthcheck: test: ["CMD-SHELL", "redis-cli ping"] + start_period: 5s + start_interval: 5s restart: unless-stopped docs: From f2662a43c5a13cf068a2bd58b227f4b5b380290f Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Fri, 7 Nov 2025 16:53:02 +0000 Subject: [PATCH 29/79] fix(backend): default example redis password should be empty --- backend/.env.example | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/.env.example b/backend/.env.example index fac58ca..1cf0df5 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -29,10 +29,10 @@ EMAIL_FROM='Your Name ' # 🔀 Email address EMAIL_REPLY_TO='your.replyto.alias.@example.com' # 🔀 Email address to which replies are sent. Can be different from the SMTP server username. # Redis settings for caching (disposable email domains, sessions, etc.) -REDIS_HOST='localhost' # 🔀 Redis server host (use 'cache' in Docker) -REDIS_PORT='6379' # 🔀 Redis server port -REDIS_DB='0' # 🔀 Redis database number (0-15) -REDIS_PASSWORD='password' # 🔀 Redis password (leave empty if no password) +REDIS_HOST='localhost' # Redis server host (use 'cache' in Docker) +REDIS_PORT='6379' # Redis server port +REDIS_DB='0' # Redis database number (0-15) +REDIS_PASSWORD='' # 🔀 Redis password (leave empty if no password) # Superuser details SUPERUSER_EMAIL='your-email@example.com' # 🔀 From d98d002144aad5ee5441937175d431989f1f84ec Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Fri, 7 Nov 2025 16:53:55 +0000 Subject: [PATCH 30/79] feature(backend): Use custom fork of fastapi-mail and pass redis client directly to email checker instead of creating double Redis connection --- .../app/api/auth/utils/email_validation.py | 6 +- backend/pyproject.toml | 4 +- backend/uv.lock | 1950 +++++++++-------- 3 files changed, 1027 insertions(+), 933 deletions(-) diff --git a/backend/app/api/auth/utils/email_validation.py b/backend/app/api/auth/utils/email_validation.py index ac2c6de..79e908b 100644 --- a/backend/app/api/auth/utils/email_validation.py +++ b/backend/app/api/auth/utils/email_validation.py @@ -34,7 +34,11 @@ async def initialize(self) -> None: Should be called during application startup. """ try: - self.checker = DefaultChecker(db_provider="redis", source=DISPOSABLE_DOMAINS_URL) + self.checker = DefaultChecker( + source=DISPOSABLE_DOMAINS_URL, + db_provider="redis", + redis_client=self.redis_client, + ) await self.checker.init_redis() logger.info("Disposable email checker initialized successfully") diff --git a/backend/pyproject.toml b/backend/pyproject.toml index fd4f4c1..cd97213 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -27,7 +27,7 @@ "cachetools>=5.5.2", "email-validator>=2.2.0", "fastapi-filter>=2.0.1", - "fastapi-mail==1.5.2", + "fastapi-mail", "fastapi-pagination>=0.13.2", # NOTE: This is a heavy dependency (~40MB) due to its use of boto3, even though we don't use any cloud storage # We should consider using a more lightweight alternative if it becomes available. @@ -249,5 +249,7 @@ default-groups = ["api", "dev", "migrations", "tests"] [tool.uv.sources] + # HACK: Fetch FastAPI-Mail from custom fork on GitHub to allow passing existing Redis client and fix compatibility issues with Pydantic > 2.12 and SQLModel (see https://github.com/fastapi/sqlmodel/issues/1623) + fastapi-mail = { git = "https://github.com/simonvanlierde/fastapi-mail", rev = "f32147ec1a450ed22262913c5ac7ec3b67dd0117" } # Fetch FastAPI-Users-DB-SQLModel from custom fork on GitHub for Pydantic V2 support fastapi-users-db-sqlmodel = { git = "https://github.com/simonvanlierde/fastapi-users-db-sqlmodel", rev = "7e9c4830e53ee20c38e3de80066cb19d7c3efc43" } diff --git a/backend/uv.lock b/backend/uv.lock index 44bede3..cfb072d 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 1 requires-python = ">=3.13" resolution-markers = [ "python_full_version >= '3.14'", @@ -8,25 +8,25 @@ resolution-markers = [ [[package]] name = "aiosmtplib" -version = "3.0.2" +version = "4.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/2a/812517f8350cd317aad2ba1ce25dfc213c6f1f2e62e1cbf662b4bdc51d34/aiosmtplib-3.0.2.tar.gz", hash = "sha256:08fd840f9dbc23258025dca229e8a8f04d2ccf3ecb1319585615bfc7933f7f47", size = 59941, upload-time = "2024-07-31T05:13:10.065Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/e1/cc58e0be242f0b410707e001ed22c689435964fcaab42108887426e44fff/aiosmtplib-4.0.2.tar.gz", hash = "sha256:f0b4933e7270a8be2b588761e5b12b7334c11890ee91987c2fb057e72f566da6", size = 61052 } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/35/441faea7a11159795881a6ec869454f40269e4e3806dced935a35d83a412/aiosmtplib-3.0.2-py3-none-any.whl", hash = "sha256:8783059603a34834c7c90ca51103c3aa129d5922003b5ce98dbaa6d4440f10fc", size = 27111, upload-time = "2024-07-31T05:13:08.515Z" }, + { url = "https://files.pythonhosted.org/packages/f1/2f/db9414bbeacee48ab0c7421a0319b361b7c15b5c3feebcd38684f5d5f849/aiosmtplib-4.0.2-py3-none-any.whl", hash = "sha256:72491f96e6de035c28d29870186782eccb2f651db9c5f8a32c9db689327f5742", size = 27048 }, ] [[package]] name = "alembic" -version = "1.17.0" +version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mako" }, { name = "sqlalchemy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6b/45/6f4555f2039f364c3ce31399529dcf48dd60726ff3715ad67f547d87dfd2/alembic-1.17.0.tar.gz", hash = "sha256:4652a0b3e19616b57d652b82bfa5e38bf5dbea0813eed971612671cb9e90c0fe", size = 1975526, upload-time = "2025-10-11T18:40:13.585Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/b6/2a81d7724c0c124edc5ec7a167e85858b6fd31b9611c6fb8ecf617b7e2d3/alembic-1.17.1.tar.gz", hash = "sha256:8a289f6778262df31571d29cca4c7fbacd2f0f582ea0816f4c399b6da7528486", size = 1981285 } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/1f/38e29b06bfed7818ebba1f84904afdc8153ef7b6c7e0d8f3bc6643f5989c/alembic-1.17.0-py3-none-any.whl", hash = "sha256:80523bc437d41b35c5db7e525ad9d908f79de65c27d6a5a5eab6df348a352d99", size = 247449, upload-time = "2025-10-11T18:40:16.288Z" }, + { url = "https://files.pythonhosted.org/packages/a5/32/7df1d81ec2e50fb661944a35183d87e62d3f6c6d9f8aff64a4f245226d55/alembic-1.17.1-py3-none-any.whl", hash = "sha256:cbc2386e60f89608bb63f30d2d6cc66c7aaed1fe105bd862828600e5ad167023", size = 247848 }, ] [[package]] @@ -37,9 +37,9 @@ dependencies = [ { name = "alembic" }, { name = "click" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d2/bb/6e5eb52a6695690f91b7f67027f7a498ecb0e307f4f2e7d0ae0f854059f5/alembic-autogen-check-1.1.1.tar.gz", hash = "sha256:cdda293a71b2413e854b07641c6f8291dffca0c5c6d0531b7b457629a30ca9cf", size = 2660, upload-time = "2019-05-10T21:45:02.015Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d2/bb/6e5eb52a6695690f91b7f67027f7a498ecb0e307f4f2e7d0ae0f854059f5/alembic-autogen-check-1.1.1.tar.gz", hash = "sha256:cdda293a71b2413e854b07641c6f8291dffca0c5c6d0531b7b457629a30ca9cf", size = 2660 } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/10/57410287f55b37aff354aa078d66f4a759b753ecb7d5aa1225174e1fd6ee/alembic_autogen_check-1.1.1-py3-none-any.whl", hash = "sha256:331c90b99cc2d1c40e69205dfd5e44b5d9c8f111e4b96244f79b303398740659", size = 3968, upload-time = "2019-05-10T21:45:01.039Z" }, + { url = "https://files.pythonhosted.org/packages/24/10/57410287f55b37aff354aa078d66f4a759b753ecb7d5aa1225174e1fd6ee/alembic_autogen_check-1.1.1-py3-none-any.whl", hash = "sha256:331c90b99cc2d1c40e69205dfd5e44b5d9c8f111e4b96244f79b303398740659", size = 3968 }, ] [[package]] @@ -50,18 +50,27 @@ dependencies = [ { name = "alembic" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/04/e465cb5c051fb056b7fadda7667b3e1fb4d32d7f19533e3bbff071c73788/alembic_postgresql_enum-1.8.0.tar.gz", hash = "sha256:132cd5fdc4a2a0b6498f3d89ea1c7b2a5ddc3281ddd84edae7259ec4c0a215a0", size = 15858, upload-time = "2025-07-20T12:25:50.626Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/04/e465cb5c051fb056b7fadda7667b3e1fb4d32d7f19533e3bbff071c73788/alembic_postgresql_enum-1.8.0.tar.gz", hash = "sha256:132cd5fdc4a2a0b6498f3d89ea1c7b2a5ddc3281ddd84edae7259ec4c0a215a0", size = 15858 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/80/4e6e841f9a0403b520b8f28650c2cdf5905e25bd4ff403b43daec580fed3/alembic_postgresql_enum-1.8.0-py3-none-any.whl", hash = "sha256:0e62833f8d1aca2c58fa09cae1d4a52472fb32d2dde32b68c84515fffcf401d5", size = 23697 }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/a6/dc46877b911e40c00d395771ea710d5e77b6de7bacd5fdcd78d70cc5a48f/annotated_doc-0.0.3.tar.gz", hash = "sha256:e18370014c70187422c33e945053ff4c286f453a984eba84d0dbfa0c935adeda", size = 5535 } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/80/4e6e841f9a0403b520b8f28650c2cdf5905e25bd4ff403b43daec580fed3/alembic_postgresql_enum-1.8.0-py3-none-any.whl", hash = "sha256:0e62833f8d1aca2c58fa09cae1d4a52472fb32d2dde32b68c84515fffcf401d5", size = 23697, upload-time = "2025-07-20T12:25:49.048Z" }, + { url = "https://files.pythonhosted.org/packages/02/b7/cf592cb5de5cb3bade3357f8d2cf42bf103bbe39f459824b4939fd212911/annotated_doc-0.0.3-py3-none-any.whl", hash = "sha256:348ec6664a76f1fd3be81f43dffbee4c7e8ce931ba71ec67cc7f4ade7fbbb580", size = 5488 }, ] [[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" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } 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" }, + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, ] [[package]] @@ -72,9 +81,9 @@ dependencies = [ { name = "idna" }, { name = "sniffio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094 } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, + { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097 }, ] [[package]] @@ -84,9 +93,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argon2-cffi-bindings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/31/fa/57ec2c6d16ecd2ba0cf15f3c7d1c3c2e7b5fcb83555ff56d7ab10888ec8f/argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08", size = 42798, upload-time = "2023-08-15T14:13:12.711Z" } +sdist = { url = "https://files.pythonhosted.org/packages/31/fa/57ec2c6d16ecd2ba0cf15f3c7d1c3c2e7b5fcb83555ff56d7ab10888ec8f/argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08", size = 42798 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/6a/e8a041599e78b6b3752da48000b14c8d1e8a04ded09c88c714ba047f34f5/argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea", size = 15124, upload-time = "2023-08-15T14:13:10.752Z" }, + { url = "https://files.pythonhosted.org/packages/a4/6a/e8a041599e78b6b3752da48000b14c8d1e8a04ded09c88c714ba047f34f5/argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea", size = 15124 }, ] [[package]] @@ -96,28 +105,28 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/3c0a35f46e52108d4707c44b95cfe2afcafc50800b5450c197454569b776/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f", size = 54393, upload-time = "2025-07-30T10:01:40.97Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f4/98bbd6ee89febd4f212696f13c03ca302b8552e7dbf9c8efa11ea4a388c3/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b", size = 29328, upload-time = "2025-07-30T10:01:41.916Z" }, - { url = "https://files.pythonhosted.org/packages/43/24/90a01c0ef12ac91a6be05969f29944643bc1e5e461155ae6559befa8f00b/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a", size = 31269, upload-time = "2025-07-30T10:01:42.716Z" }, - { url = "https://files.pythonhosted.org/packages/d4/d3/942aa10782b2697eee7af5e12eeff5ebb325ccfb86dd8abda54174e377e4/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44", size = 86558, upload-time = "2025-07-30T10:01:43.943Z" }, - { url = "https://files.pythonhosted.org/packages/0d/82/b484f702fec5536e71836fc2dbc8c5267b3f6e78d2d539b4eaa6f0db8bf8/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb", size = 92364, upload-time = "2025-07-30T10:01:44.887Z" }, - { url = "https://files.pythonhosted.org/packages/c9/c1/a606ff83b3f1735f3759ad0f2cd9e038a0ad11a3de3b6c673aa41c24bb7b/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92", size = 85637, upload-time = "2025-07-30T10:01:46.225Z" }, - { url = "https://files.pythonhosted.org/packages/44/b4/678503f12aceb0262f84fa201f6027ed77d71c5019ae03b399b97caa2f19/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85", size = 91934, upload-time = "2025-07-30T10:01:47.203Z" }, - { url = "https://files.pythonhosted.org/packages/f0/c7/f36bd08ef9bd9f0a9cff9428406651f5937ce27b6c5b07b92d41f91ae541/argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f", size = 28158, upload-time = "2025-07-30T10:01:48.341Z" }, - { url = "https://files.pythonhosted.org/packages/b3/80/0106a7448abb24a2c467bf7d527fe5413b7fdfa4ad6d6a96a43a62ef3988/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6", size = 32597, upload-time = "2025-07-30T10:01:49.112Z" }, - { url = "https://files.pythonhosted.org/packages/05/b8/d663c9caea07e9180b2cb662772865230715cbd573ba3b5e81793d580316/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623", size = 28231, upload-time = "2025-07-30T10:01:49.92Z" }, - { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" }, - { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" }, - { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" }, - { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246, upload-time = "2025-07-30T10:01:54.145Z" }, - { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126, upload-time = "2025-07-30T10:01:55.074Z" }, - { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343, upload-time = "2025-07-30T10:01:56.007Z" }, - { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777, upload-time = "2025-07-30T10:01:56.943Z" }, - { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180, upload-time = "2025-07-30T10:01:57.759Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715, upload-time = "2025-07-30T10:01:58.56Z" }, - { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/3c0a35f46e52108d4707c44b95cfe2afcafc50800b5450c197454569b776/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f", size = 54393 }, + { url = "https://files.pythonhosted.org/packages/9d/f4/98bbd6ee89febd4f212696f13c03ca302b8552e7dbf9c8efa11ea4a388c3/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b", size = 29328 }, + { url = "https://files.pythonhosted.org/packages/43/24/90a01c0ef12ac91a6be05969f29944643bc1e5e461155ae6559befa8f00b/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a", size = 31269 }, + { url = "https://files.pythonhosted.org/packages/d4/d3/942aa10782b2697eee7af5e12eeff5ebb325ccfb86dd8abda54174e377e4/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44", size = 86558 }, + { url = "https://files.pythonhosted.org/packages/0d/82/b484f702fec5536e71836fc2dbc8c5267b3f6e78d2d539b4eaa6f0db8bf8/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb", size = 92364 }, + { url = "https://files.pythonhosted.org/packages/c9/c1/a606ff83b3f1735f3759ad0f2cd9e038a0ad11a3de3b6c673aa41c24bb7b/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92", size = 85637 }, + { url = "https://files.pythonhosted.org/packages/44/b4/678503f12aceb0262f84fa201f6027ed77d71c5019ae03b399b97caa2f19/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85", size = 91934 }, + { url = "https://files.pythonhosted.org/packages/f0/c7/f36bd08ef9bd9f0a9cff9428406651f5937ce27b6c5b07b92d41f91ae541/argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f", size = 28158 }, + { url = "https://files.pythonhosted.org/packages/b3/80/0106a7448abb24a2c467bf7d527fe5413b7fdfa4ad6d6a96a43a62ef3988/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6", size = 32597 }, + { url = "https://files.pythonhosted.org/packages/05/b8/d663c9caea07e9180b2cb662772865230715cbd573ba3b5e81793d580316/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623", size = 28231 }, + { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121 }, + { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177 }, + { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090 }, + { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246 }, + { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126 }, + { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343 }, + { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777 }, + { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180 }, + { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715 }, + { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149 }, ] [[package]] @@ -127,75 +136,75 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/cf/17f8a6b6b97f77b5981fbce1266913e718daaa3467b46f60a785cbaadc29/asyncache-0.3.1.tar.gz", hash = "sha256:9a1e60a75668e794657489bdea6540ee7e3259c483517b934670db7600bf5035", size = 3797, upload-time = "2022-11-15T10:06:47.476Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/cf/17f8a6b6b97f77b5981fbce1266913e718daaa3467b46f60a785cbaadc29/asyncache-0.3.1.tar.gz", hash = "sha256:9a1e60a75668e794657489bdea6540ee7e3259c483517b934670db7600bf5035", size = 3797 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/94/51927deb4f40872361ec4f5534f68f7a9ce81c4ef20bf5cd765307f4c15d/asyncache-0.3.1-py3-none-any.whl", hash = "sha256:ef20a1024d265090dd1e0785c961cf98b9c32cc7d9478973dcf25ac1b80011f5", size = 3722, upload-time = "2022-11-15T10:06:45.546Z" }, + { url = "https://files.pythonhosted.org/packages/2f/94/51927deb4f40872361ec4f5534f68f7a9ce81c4ef20bf5cd765307f4c15d/asyncache-0.3.1-py3-none-any.whl", hash = "sha256:ef20a1024d265090dd1e0785c961cf98b9c32cc7d9478973dcf25ac1b80011f5", size = 3722 }, ] [[package]] name = "asyncpg" version = "0.30.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/4c/7c991e080e106d854809030d8584e15b2e996e26f16aee6d757e387bc17d/asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851", size = 957746, upload-time = "2024-10-20T00:30:41.127Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/4c/7c991e080e106d854809030d8584e15b2e996e26f16aee6d757e387bc17d/asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851", size = 957746 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/22/e20602e1218dc07692acf70d5b902be820168d6282e69ef0d3cb920dc36f/asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70", size = 670373, upload-time = "2024-10-20T00:29:55.165Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b3/0cf269a9d647852a95c06eb00b815d0b95a4eb4b55aa2d6ba680971733b9/asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3", size = 634745, upload-time = "2024-10-20T00:29:57.14Z" }, - { url = "https://files.pythonhosted.org/packages/8e/6d/a4f31bf358ce8491d2a31bfe0d7bcf25269e80481e49de4d8616c4295a34/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33", size = 3512103, upload-time = "2024-10-20T00:29:58.499Z" }, - { url = "https://files.pythonhosted.org/packages/96/19/139227a6e67f407b9c386cb594d9628c6c78c9024f26df87c912fabd4368/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4", size = 3592471, upload-time = "2024-10-20T00:30:00.354Z" }, - { url = "https://files.pythonhosted.org/packages/67/e4/ab3ca38f628f53f0fd28d3ff20edff1c975dd1cb22482e0061916b4b9a74/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4", size = 3496253, upload-time = "2024-10-20T00:30:02.794Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5f/0bf65511d4eeac3a1f41c54034a492515a707c6edbc642174ae79034d3ba/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba", size = 3662720, upload-time = "2024-10-20T00:30:04.501Z" }, - { url = "https://files.pythonhosted.org/packages/e7/31/1513d5a6412b98052c3ed9158d783b1e09d0910f51fbe0e05f56cc370bc4/asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590", size = 560404, upload-time = "2024-10-20T00:30:06.537Z" }, - { url = "https://files.pythonhosted.org/packages/c8/a4/cec76b3389c4c5ff66301cd100fe88c318563ec8a520e0b2e792b5b84972/asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e", size = 621623, upload-time = "2024-10-20T00:30:09.024Z" }, + { url = "https://files.pythonhosted.org/packages/3a/22/e20602e1218dc07692acf70d5b902be820168d6282e69ef0d3cb920dc36f/asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70", size = 670373 }, + { url = "https://files.pythonhosted.org/packages/3d/b3/0cf269a9d647852a95c06eb00b815d0b95a4eb4b55aa2d6ba680971733b9/asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3", size = 634745 }, + { url = "https://files.pythonhosted.org/packages/8e/6d/a4f31bf358ce8491d2a31bfe0d7bcf25269e80481e49de4d8616c4295a34/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33", size = 3512103 }, + { url = "https://files.pythonhosted.org/packages/96/19/139227a6e67f407b9c386cb594d9628c6c78c9024f26df87c912fabd4368/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4", size = 3592471 }, + { url = "https://files.pythonhosted.org/packages/67/e4/ab3ca38f628f53f0fd28d3ff20edff1c975dd1cb22482e0061916b4b9a74/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4", size = 3496253 }, + { url = "https://files.pythonhosted.org/packages/ef/5f/0bf65511d4eeac3a1f41c54034a492515a707c6edbc642174ae79034d3ba/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba", size = 3662720 }, + { url = "https://files.pythonhosted.org/packages/e7/31/1513d5a6412b98052c3ed9158d783b1e09d0910f51fbe0e05f56cc370bc4/asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590", size = 560404 }, + { url = "https://files.pythonhosted.org/packages/c8/a4/cec76b3389c4c5ff66301cd100fe88c318563ec8a520e0b2e792b5b84972/asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e", size = 621623 }, ] [[package]] name = "bcrypt" version = "4.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/5d/6d7433e0f3cd46ce0b43cd65e1db465ea024dbb8216fb2404e919c2ad77b/bcrypt-4.3.0.tar.gz", hash = "sha256:3a3fd2204178b6d2adcf09cb4f6426ffef54762577a7c9b54c159008cb288c18", size = 25697, upload-time = "2025-02-28T01:24:09.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/2c/3d44e853d1fe969d229bd58d39ae6902b3d924af0e2b5a60d17d4b809ded/bcrypt-4.3.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281", size = 483719, upload-time = "2025-02-28T01:22:34.539Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e2/58ff6e2a22eca2e2cff5370ae56dba29d70b1ea6fc08ee9115c3ae367795/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb", size = 272001, upload-time = "2025-02-28T01:22:38.078Z" }, - { url = "https://files.pythonhosted.org/packages/37/1f/c55ed8dbe994b1d088309e366749633c9eb90d139af3c0a50c102ba68a1a/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59e1aa0e2cd871b08ca146ed08445038f42ff75968c7ae50d2fdd7860ade2180", size = 277451, upload-time = "2025-02-28T01:22:40.787Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1c/794feb2ecf22fe73dcfb697ea7057f632061faceb7dcf0f155f3443b4d79/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:0042b2e342e9ae3d2ed22727c1262f76cc4f345683b5c1715f0250cf4277294f", size = 272792, upload-time = "2025-02-28T01:22:43.144Z" }, - { url = "https://files.pythonhosted.org/packages/13/b7/0b289506a3f3598c2ae2bdfa0ea66969812ed200264e3f61df77753eee6d/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74a8d21a09f5e025a9a23e7c0fd2c7fe8e7503e4d356c0a2c1486ba010619f09", size = 289752, upload-time = "2025-02-28T01:22:45.56Z" }, - { url = "https://files.pythonhosted.org/packages/dc/24/d0fb023788afe9e83cc118895a9f6c57e1044e7e1672f045e46733421fe6/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:0142b2cb84a009f8452c8c5a33ace5e3dfec4159e7735f5afe9a4d50a8ea722d", size = 277762, upload-time = "2025-02-28T01:22:47.023Z" }, - { url = "https://files.pythonhosted.org/packages/e4/38/cde58089492e55ac4ef6c49fea7027600c84fd23f7520c62118c03b4625e/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:12fa6ce40cde3f0b899729dbd7d5e8811cb892d31b6f7d0334a1f37748b789fd", size = 272384, upload-time = "2025-02-28T01:22:49.221Z" }, - { url = "https://files.pythonhosted.org/packages/de/6a/d5026520843490cfc8135d03012a413e4532a400e471e6188b01b2de853f/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:5bd3cca1f2aa5dbcf39e2aa13dd094ea181f48959e1071265de49cc2b82525af", size = 277329, upload-time = "2025-02-28T01:22:51.603Z" }, - { url = "https://files.pythonhosted.org/packages/b3/a3/4fc5255e60486466c389e28c12579d2829b28a527360e9430b4041df4cf9/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:335a420cfd63fc5bc27308e929bee231c15c85cc4c496610ffb17923abf7f231", size = 305241, upload-time = "2025-02-28T01:22:53.283Z" }, - { url = "https://files.pythonhosted.org/packages/c7/15/2b37bc07d6ce27cc94e5b10fd5058900eb8fb11642300e932c8c82e25c4a/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:0e30e5e67aed0187a1764911af023043b4542e70a7461ad20e837e94d23e1d6c", size = 309617, upload-time = "2025-02-28T01:22:55.461Z" }, - { url = "https://files.pythonhosted.org/packages/5f/1f/99f65edb09e6c935232ba0430c8c13bb98cb3194b6d636e61d93fe60ac59/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b8d62290ebefd49ee0b3ce7500f5dbdcf13b81402c05f6dafab9a1e1b27212f", size = 335751, upload-time = "2025-02-28T01:22:57.81Z" }, - { url = "https://files.pythonhosted.org/packages/00/1b/b324030c706711c99769988fcb694b3cb23f247ad39a7823a78e361bdbb8/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2ef6630e0ec01376f59a006dc72918b1bf436c3b571b80fa1968d775fa02fe7d", size = 355965, upload-time = "2025-02-28T01:22:59.181Z" }, - { url = "https://files.pythonhosted.org/packages/aa/dd/20372a0579dd915dfc3b1cd4943b3bca431866fcb1dfdfd7518c3caddea6/bcrypt-4.3.0-cp313-cp313t-win32.whl", hash = "sha256:7a4be4cbf241afee43f1c3969b9103a41b40bcb3a3f467ab19f891d9bc4642e4", size = 155316, upload-time = "2025-02-28T01:23:00.763Z" }, - { url = "https://files.pythonhosted.org/packages/6d/52/45d969fcff6b5577c2bf17098dc36269b4c02197d551371c023130c0f890/bcrypt-4.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c1949bf259a388863ced887c7861da1df681cb2388645766c89fdfd9004c669", size = 147752, upload-time = "2025-02-28T01:23:02.908Z" }, - { url = "https://files.pythonhosted.org/packages/11/22/5ada0b9af72b60cbc4c9a399fdde4af0feaa609d27eb0adc61607997a3fa/bcrypt-4.3.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:f81b0ed2639568bf14749112298f9e4e2b28853dab50a8b357e31798686a036d", size = 498019, upload-time = "2025-02-28T01:23:05.838Z" }, - { url = "https://files.pythonhosted.org/packages/b8/8c/252a1edc598dc1ce57905be173328eda073083826955ee3c97c7ff5ba584/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:864f8f19adbe13b7de11ba15d85d4a428c7e2f344bac110f667676a0ff84924b", size = 279174, upload-time = "2025-02-28T01:23:07.274Z" }, - { url = "https://files.pythonhosted.org/packages/29/5b/4547d5c49b85f0337c13929f2ccbe08b7283069eea3550a457914fc078aa/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e36506d001e93bffe59754397572f21bb5dc7c83f54454c990c74a468cd589e", size = 283870, upload-time = "2025-02-28T01:23:09.151Z" }, - { url = "https://files.pythonhosted.org/packages/be/21/7dbaf3fa1745cb63f776bb046e481fbababd7d344c5324eab47f5ca92dd2/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:842d08d75d9fe9fb94b18b071090220697f9f184d4547179b60734846461ed59", size = 279601, upload-time = "2025-02-28T01:23:11.461Z" }, - { url = "https://files.pythonhosted.org/packages/6d/64/e042fc8262e971347d9230d9abbe70d68b0a549acd8611c83cebd3eaec67/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7c03296b85cb87db865d91da79bf63d5609284fc0cab9472fdd8367bbd830753", size = 297660, upload-time = "2025-02-28T01:23:12.989Z" }, - { url = "https://files.pythonhosted.org/packages/50/b8/6294eb84a3fef3b67c69b4470fcdd5326676806bf2519cda79331ab3c3a9/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:62f26585e8b219cdc909b6a0069efc5e4267e25d4a3770a364ac58024f62a761", size = 284083, upload-time = "2025-02-28T01:23:14.5Z" }, - { url = "https://files.pythonhosted.org/packages/62/e6/baff635a4f2c42e8788fe1b1633911c38551ecca9a749d1052d296329da6/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:beeefe437218a65322fbd0069eb437e7c98137e08f22c4660ac2dc795c31f8bb", size = 279237, upload-time = "2025-02-28T01:23:16.686Z" }, - { url = "https://files.pythonhosted.org/packages/39/48/46f623f1b0c7dc2e5de0b8af5e6f5ac4cc26408ac33f3d424e5ad8da4a90/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:97eea7408db3a5bcce4a55d13245ab3fa566e23b4c67cd227062bb49e26c585d", size = 283737, upload-time = "2025-02-28T01:23:18.897Z" }, - { url = "https://files.pythonhosted.org/packages/49/8b/70671c3ce9c0fca4a6cc3cc6ccbaa7e948875a2e62cbd146e04a4011899c/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:191354ebfe305e84f344c5964c7cd5f924a3bfc5d405c75ad07f232b6dffb49f", size = 312741, upload-time = "2025-02-28T01:23:21.041Z" }, - { url = "https://files.pythonhosted.org/packages/27/fb/910d3a1caa2d249b6040a5caf9f9866c52114d51523ac2fb47578a27faee/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:41261d64150858eeb5ff43c753c4b216991e0ae16614a308a15d909503617732", size = 316472, upload-time = "2025-02-28T01:23:23.183Z" }, - { url = "https://files.pythonhosted.org/packages/dc/cf/7cf3a05b66ce466cfb575dbbda39718d45a609daa78500f57fa9f36fa3c0/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:33752b1ba962ee793fa2b6321404bf20011fe45b9afd2a842139de3011898fef", size = 343606, upload-time = "2025-02-28T01:23:25.361Z" }, - { url = "https://files.pythonhosted.org/packages/e3/b8/e970ecc6d7e355c0d892b7f733480f4aa8509f99b33e71550242cf0b7e63/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:50e6e80a4bfd23a25f5c05b90167c19030cf9f87930f7cb2eacb99f45d1c3304", size = 362867, upload-time = "2025-02-28T01:23:26.875Z" }, - { url = "https://files.pythonhosted.org/packages/a9/97/8d3118efd8354c555a3422d544163f40d9f236be5b96c714086463f11699/bcrypt-4.3.0-cp38-abi3-win32.whl", hash = "sha256:67a561c4d9fb9465ec866177e7aebcad08fe23aaf6fbd692a6fab69088abfc51", size = 160589, upload-time = "2025-02-28T01:23:28.381Z" }, - { url = "https://files.pythonhosted.org/packages/29/07/416f0b99f7f3997c69815365babbc2e8754181a4b1899d921b3c7d5b6f12/bcrypt-4.3.0-cp38-abi3-win_amd64.whl", hash = "sha256:584027857bc2843772114717a7490a37f68da563b3620f78a849bcb54dc11e62", size = 152794, upload-time = "2025-02-28T01:23:30.187Z" }, - { url = "https://files.pythonhosted.org/packages/6e/c1/3fa0e9e4e0bfd3fd77eb8b52ec198fd6e1fd7e9402052e43f23483f956dd/bcrypt-4.3.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d3efb1157edebfd9128e4e46e2ac1a64e0c1fe46fb023158a407c7892b0f8c3", size = 498969, upload-time = "2025-02-28T01:23:31.945Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d4/755ce19b6743394787fbd7dff6bf271b27ee9b5912a97242e3caf125885b/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08bacc884fd302b611226c01014eca277d48f0a05187666bca23aac0dad6fe24", size = 279158, upload-time = "2025-02-28T01:23:34.161Z" }, - { url = "https://files.pythonhosted.org/packages/9b/5d/805ef1a749c965c46b28285dfb5cd272a7ed9fa971f970435a5133250182/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6746e6fec103fcd509b96bacdfdaa2fbde9a553245dbada284435173a6f1aef", size = 284285, upload-time = "2025-02-28T01:23:35.765Z" }, - { url = "https://files.pythonhosted.org/packages/ab/2b/698580547a4a4988e415721b71eb45e80c879f0fb04a62da131f45987b96/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:afe327968aaf13fc143a56a3360cb27d4ad0345e34da12c7290f1b00b8fe9a8b", size = 279583, upload-time = "2025-02-28T01:23:38.021Z" }, - { url = "https://files.pythonhosted.org/packages/f2/87/62e1e426418204db520f955ffd06f1efd389feca893dad7095bf35612eec/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d9af79d322e735b1fc33404b5765108ae0ff232d4b54666d46730f8ac1a43676", size = 297896, upload-time = "2025-02-28T01:23:39.575Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c6/8fedca4c2ada1b6e889c52d2943b2f968d3427e5d65f595620ec4c06fa2f/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f1e3ffa1365e8702dc48c8b360fef8d7afeca482809c5e45e653af82ccd088c1", size = 284492, upload-time = "2025-02-28T01:23:40.901Z" }, - { url = "https://files.pythonhosted.org/packages/4d/4d/c43332dcaaddb7710a8ff5269fcccba97ed3c85987ddaa808db084267b9a/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3004df1b323d10021fda07a813fd33e0fd57bef0e9a480bb143877f6cba996fe", size = 279213, upload-time = "2025-02-28T01:23:42.653Z" }, - { url = "https://files.pythonhosted.org/packages/dc/7f/1e36379e169a7df3a14a1c160a49b7b918600a6008de43ff20d479e6f4b5/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:531457e5c839d8caea9b589a1bcfe3756b0547d7814e9ce3d437f17da75c32b0", size = 284162, upload-time = "2025-02-28T01:23:43.964Z" }, - { url = "https://files.pythonhosted.org/packages/1c/0a/644b2731194b0d7646f3210dc4d80c7fee3ecb3a1f791a6e0ae6bb8684e3/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:17a854d9a7a476a89dcef6c8bd119ad23e0f82557afbd2c442777a16408e614f", size = 312856, upload-time = "2025-02-28T01:23:46.011Z" }, - { url = "https://files.pythonhosted.org/packages/dc/62/2a871837c0bb6ab0c9a88bf54de0fc021a6a08832d4ea313ed92a669d437/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6fb1fd3ab08c0cbc6826a2e0447610c6f09e983a281b919ed721ad32236b8b23", size = 316726, upload-time = "2025-02-28T01:23:47.575Z" }, - { url = "https://files.pythonhosted.org/packages/0c/a1/9898ea3faac0b156d457fd73a3cb9c2855c6fd063e44b8522925cdd8ce46/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e965a9c1e9a393b8005031ff52583cedc15b7884fce7deb8b0346388837d6cfe", size = 343664, upload-time = "2025-02-28T01:23:49.059Z" }, - { url = "https://files.pythonhosted.org/packages/40/f2/71b4ed65ce38982ecdda0ff20c3ad1b15e71949c78b2c053df53629ce940/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:79e70b8342a33b52b55d93b3a59223a844962bef479f6a0ea318ebbcadf71505", size = 363128, upload-time = "2025-02-28T01:23:50.399Z" }, - { url = "https://files.pythonhosted.org/packages/11/99/12f6a58eca6dea4be992d6c681b7ec9410a1d9f5cf368c61437e31daa879/bcrypt-4.3.0-cp39-abi3-win32.whl", hash = "sha256:b4d4e57f0a63fd0b358eb765063ff661328f69a04494427265950c71b992a39a", size = 160598, upload-time = "2025-02-28T01:23:51.775Z" }, - { url = "https://files.pythonhosted.org/packages/a9/cf/45fb5261ece3e6b9817d3d82b2f343a505fd58674a92577923bc500bd1aa/bcrypt-4.3.0-cp39-abi3-win_amd64.whl", hash = "sha256:e53e074b120f2877a35cc6c736b8eb161377caae8925c17688bd46ba56daaa5b", size = 152799, upload-time = "2025-02-28T01:23:53.139Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/bb/5d/6d7433e0f3cd46ce0b43cd65e1db465ea024dbb8216fb2404e919c2ad77b/bcrypt-4.3.0.tar.gz", hash = "sha256:3a3fd2204178b6d2adcf09cb4f6426ffef54762577a7c9b54c159008cb288c18", size = 25697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/2c/3d44e853d1fe969d229bd58d39ae6902b3d924af0e2b5a60d17d4b809ded/bcrypt-4.3.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281", size = 483719 }, + { url = "https://files.pythonhosted.org/packages/a1/e2/58ff6e2a22eca2e2cff5370ae56dba29d70b1ea6fc08ee9115c3ae367795/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb", size = 272001 }, + { url = "https://files.pythonhosted.org/packages/37/1f/c55ed8dbe994b1d088309e366749633c9eb90d139af3c0a50c102ba68a1a/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59e1aa0e2cd871b08ca146ed08445038f42ff75968c7ae50d2fdd7860ade2180", size = 277451 }, + { url = "https://files.pythonhosted.org/packages/d7/1c/794feb2ecf22fe73dcfb697ea7057f632061faceb7dcf0f155f3443b4d79/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:0042b2e342e9ae3d2ed22727c1262f76cc4f345683b5c1715f0250cf4277294f", size = 272792 }, + { url = "https://files.pythonhosted.org/packages/13/b7/0b289506a3f3598c2ae2bdfa0ea66969812ed200264e3f61df77753eee6d/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74a8d21a09f5e025a9a23e7c0fd2c7fe8e7503e4d356c0a2c1486ba010619f09", size = 289752 }, + { url = "https://files.pythonhosted.org/packages/dc/24/d0fb023788afe9e83cc118895a9f6c57e1044e7e1672f045e46733421fe6/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:0142b2cb84a009f8452c8c5a33ace5e3dfec4159e7735f5afe9a4d50a8ea722d", size = 277762 }, + { url = "https://files.pythonhosted.org/packages/e4/38/cde58089492e55ac4ef6c49fea7027600c84fd23f7520c62118c03b4625e/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:12fa6ce40cde3f0b899729dbd7d5e8811cb892d31b6f7d0334a1f37748b789fd", size = 272384 }, + { url = "https://files.pythonhosted.org/packages/de/6a/d5026520843490cfc8135d03012a413e4532a400e471e6188b01b2de853f/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:5bd3cca1f2aa5dbcf39e2aa13dd094ea181f48959e1071265de49cc2b82525af", size = 277329 }, + { url = "https://files.pythonhosted.org/packages/b3/a3/4fc5255e60486466c389e28c12579d2829b28a527360e9430b4041df4cf9/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:335a420cfd63fc5bc27308e929bee231c15c85cc4c496610ffb17923abf7f231", size = 305241 }, + { url = "https://files.pythonhosted.org/packages/c7/15/2b37bc07d6ce27cc94e5b10fd5058900eb8fb11642300e932c8c82e25c4a/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:0e30e5e67aed0187a1764911af023043b4542e70a7461ad20e837e94d23e1d6c", size = 309617 }, + { url = "https://files.pythonhosted.org/packages/5f/1f/99f65edb09e6c935232ba0430c8c13bb98cb3194b6d636e61d93fe60ac59/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b8d62290ebefd49ee0b3ce7500f5dbdcf13b81402c05f6dafab9a1e1b27212f", size = 335751 }, + { url = "https://files.pythonhosted.org/packages/00/1b/b324030c706711c99769988fcb694b3cb23f247ad39a7823a78e361bdbb8/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2ef6630e0ec01376f59a006dc72918b1bf436c3b571b80fa1968d775fa02fe7d", size = 355965 }, + { url = "https://files.pythonhosted.org/packages/aa/dd/20372a0579dd915dfc3b1cd4943b3bca431866fcb1dfdfd7518c3caddea6/bcrypt-4.3.0-cp313-cp313t-win32.whl", hash = "sha256:7a4be4cbf241afee43f1c3969b9103a41b40bcb3a3f467ab19f891d9bc4642e4", size = 155316 }, + { url = "https://files.pythonhosted.org/packages/6d/52/45d969fcff6b5577c2bf17098dc36269b4c02197d551371c023130c0f890/bcrypt-4.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c1949bf259a388863ced887c7861da1df681cb2388645766c89fdfd9004c669", size = 147752 }, + { url = "https://files.pythonhosted.org/packages/11/22/5ada0b9af72b60cbc4c9a399fdde4af0feaa609d27eb0adc61607997a3fa/bcrypt-4.3.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:f81b0ed2639568bf14749112298f9e4e2b28853dab50a8b357e31798686a036d", size = 498019 }, + { url = "https://files.pythonhosted.org/packages/b8/8c/252a1edc598dc1ce57905be173328eda073083826955ee3c97c7ff5ba584/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:864f8f19adbe13b7de11ba15d85d4a428c7e2f344bac110f667676a0ff84924b", size = 279174 }, + { url = "https://files.pythonhosted.org/packages/29/5b/4547d5c49b85f0337c13929f2ccbe08b7283069eea3550a457914fc078aa/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e36506d001e93bffe59754397572f21bb5dc7c83f54454c990c74a468cd589e", size = 283870 }, + { url = "https://files.pythonhosted.org/packages/be/21/7dbaf3fa1745cb63f776bb046e481fbababd7d344c5324eab47f5ca92dd2/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:842d08d75d9fe9fb94b18b071090220697f9f184d4547179b60734846461ed59", size = 279601 }, + { url = "https://files.pythonhosted.org/packages/6d/64/e042fc8262e971347d9230d9abbe70d68b0a549acd8611c83cebd3eaec67/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7c03296b85cb87db865d91da79bf63d5609284fc0cab9472fdd8367bbd830753", size = 297660 }, + { url = "https://files.pythonhosted.org/packages/50/b8/6294eb84a3fef3b67c69b4470fcdd5326676806bf2519cda79331ab3c3a9/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:62f26585e8b219cdc909b6a0069efc5e4267e25d4a3770a364ac58024f62a761", size = 284083 }, + { url = "https://files.pythonhosted.org/packages/62/e6/baff635a4f2c42e8788fe1b1633911c38551ecca9a749d1052d296329da6/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:beeefe437218a65322fbd0069eb437e7c98137e08f22c4660ac2dc795c31f8bb", size = 279237 }, + { url = "https://files.pythonhosted.org/packages/39/48/46f623f1b0c7dc2e5de0b8af5e6f5ac4cc26408ac33f3d424e5ad8da4a90/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:97eea7408db3a5bcce4a55d13245ab3fa566e23b4c67cd227062bb49e26c585d", size = 283737 }, + { url = "https://files.pythonhosted.org/packages/49/8b/70671c3ce9c0fca4a6cc3cc6ccbaa7e948875a2e62cbd146e04a4011899c/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:191354ebfe305e84f344c5964c7cd5f924a3bfc5d405c75ad07f232b6dffb49f", size = 312741 }, + { url = "https://files.pythonhosted.org/packages/27/fb/910d3a1caa2d249b6040a5caf9f9866c52114d51523ac2fb47578a27faee/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:41261d64150858eeb5ff43c753c4b216991e0ae16614a308a15d909503617732", size = 316472 }, + { url = "https://files.pythonhosted.org/packages/dc/cf/7cf3a05b66ce466cfb575dbbda39718d45a609daa78500f57fa9f36fa3c0/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:33752b1ba962ee793fa2b6321404bf20011fe45b9afd2a842139de3011898fef", size = 343606 }, + { url = "https://files.pythonhosted.org/packages/e3/b8/e970ecc6d7e355c0d892b7f733480f4aa8509f99b33e71550242cf0b7e63/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:50e6e80a4bfd23a25f5c05b90167c19030cf9f87930f7cb2eacb99f45d1c3304", size = 362867 }, + { url = "https://files.pythonhosted.org/packages/a9/97/8d3118efd8354c555a3422d544163f40d9f236be5b96c714086463f11699/bcrypt-4.3.0-cp38-abi3-win32.whl", hash = "sha256:67a561c4d9fb9465ec866177e7aebcad08fe23aaf6fbd692a6fab69088abfc51", size = 160589 }, + { url = "https://files.pythonhosted.org/packages/29/07/416f0b99f7f3997c69815365babbc2e8754181a4b1899d921b3c7d5b6f12/bcrypt-4.3.0-cp38-abi3-win_amd64.whl", hash = "sha256:584027857bc2843772114717a7490a37f68da563b3620f78a849bcb54dc11e62", size = 152794 }, + { url = "https://files.pythonhosted.org/packages/6e/c1/3fa0e9e4e0bfd3fd77eb8b52ec198fd6e1fd7e9402052e43f23483f956dd/bcrypt-4.3.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d3efb1157edebfd9128e4e46e2ac1a64e0c1fe46fb023158a407c7892b0f8c3", size = 498969 }, + { url = "https://files.pythonhosted.org/packages/ce/d4/755ce19b6743394787fbd7dff6bf271b27ee9b5912a97242e3caf125885b/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08bacc884fd302b611226c01014eca277d48f0a05187666bca23aac0dad6fe24", size = 279158 }, + { url = "https://files.pythonhosted.org/packages/9b/5d/805ef1a749c965c46b28285dfb5cd272a7ed9fa971f970435a5133250182/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6746e6fec103fcd509b96bacdfdaa2fbde9a553245dbada284435173a6f1aef", size = 284285 }, + { url = "https://files.pythonhosted.org/packages/ab/2b/698580547a4a4988e415721b71eb45e80c879f0fb04a62da131f45987b96/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:afe327968aaf13fc143a56a3360cb27d4ad0345e34da12c7290f1b00b8fe9a8b", size = 279583 }, + { url = "https://files.pythonhosted.org/packages/f2/87/62e1e426418204db520f955ffd06f1efd389feca893dad7095bf35612eec/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d9af79d322e735b1fc33404b5765108ae0ff232d4b54666d46730f8ac1a43676", size = 297896 }, + { url = "https://files.pythonhosted.org/packages/cb/c6/8fedca4c2ada1b6e889c52d2943b2f968d3427e5d65f595620ec4c06fa2f/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f1e3ffa1365e8702dc48c8b360fef8d7afeca482809c5e45e653af82ccd088c1", size = 284492 }, + { url = "https://files.pythonhosted.org/packages/4d/4d/c43332dcaaddb7710a8ff5269fcccba97ed3c85987ddaa808db084267b9a/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3004df1b323d10021fda07a813fd33e0fd57bef0e9a480bb143877f6cba996fe", size = 279213 }, + { url = "https://files.pythonhosted.org/packages/dc/7f/1e36379e169a7df3a14a1c160a49b7b918600a6008de43ff20d479e6f4b5/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:531457e5c839d8caea9b589a1bcfe3756b0547d7814e9ce3d437f17da75c32b0", size = 284162 }, + { url = "https://files.pythonhosted.org/packages/1c/0a/644b2731194b0d7646f3210dc4d80c7fee3ecb3a1f791a6e0ae6bb8684e3/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:17a854d9a7a476a89dcef6c8bd119ad23e0f82557afbd2c442777a16408e614f", size = 312856 }, + { url = "https://files.pythonhosted.org/packages/dc/62/2a871837c0bb6ab0c9a88bf54de0fc021a6a08832d4ea313ed92a669d437/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6fb1fd3ab08c0cbc6826a2e0447610c6f09e983a281b919ed721ad32236b8b23", size = 316726 }, + { url = "https://files.pythonhosted.org/packages/0c/a1/9898ea3faac0b156d457fd73a3cb9c2855c6fd063e44b8522925cdd8ce46/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e965a9c1e9a393b8005031ff52583cedc15b7884fce7deb8b0346388837d6cfe", size = 343664 }, + { url = "https://files.pythonhosted.org/packages/40/f2/71b4ed65ce38982ecdda0ff20c3ad1b15e71949c78b2c053df53629ce940/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:79e70b8342a33b52b55d93b3a59223a844962bef479f6a0ea318ebbcadf71505", size = 363128 }, + { url = "https://files.pythonhosted.org/packages/11/99/12f6a58eca6dea4be992d6c681b7ec9410a1d9f5cf368c61437e31daa879/bcrypt-4.3.0-cp39-abi3-win32.whl", hash = "sha256:b4d4e57f0a63fd0b358eb765063ff661328f69a04494427265950c71b992a39a", size = 160598 }, + { url = "https://files.pythonhosted.org/packages/a9/cf/45fb5261ece3e6b9817d3d82b2f343a505fd58674a92577923bc500bd1aa/bcrypt-4.3.0-cp39-abi3-win_amd64.whl", hash = "sha256:e53e074b120f2877a35cc6c736b8eb161377caae8925c17688bd46ba56daaa5b", size = 152799 }, ] [[package]] @@ -206,64 +215,64 @@ dependencies = [ { name = "soupsieve" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822, upload-time = "2025-09-29T10:05:42.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822 } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392, upload-time = "2025-09-29T10:05:43.771Z" }, + { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392 }, ] [[package]] name = "blinker" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 }, ] [[package]] name = "boto3" -version = "1.40.55" +version = "1.40.68" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/d8/a279c054e0c9731172f05b3d118f3ffc9d74806657f84fc0c93c42d1bb5d/boto3-1.40.55.tar.gz", hash = "sha256:27e35b4fa9edd414ce06c1a748bf57cacd8203271847d93fc1053e4a4ec6e1a9", size = 111590, upload-time = "2025-10-17T19:34:56.753Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/3e/6c8ab966798f4e07651009ad08efc3ed4ffccf2662318790574695c740f7/boto3-1.40.68.tar.gz", hash = "sha256:c7994989e5bbba071b7c742adfba35773cf03e87f5d3f9f2b0a18c1664417b61", size = 111629 } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/8c/559c6145d857ed953536a83f3a94915bbd5d3d2d406db1abf8bf40be7645/boto3-1.40.55-py3-none-any.whl", hash = "sha256:2e30f5a0d49e107b8a5c0c487891afd300bfa410e1d918bf187ae45ac3839332", size = 139322, upload-time = "2025-10-17T19:34:55.028Z" }, + { url = "https://files.pythonhosted.org/packages/07/e6/b9df94d3a51ad658ef1974da6c0d7401b6aed7be50a2ee57bf1de1ef9517/boto3-1.40.68-py3-none-any.whl", hash = "sha256:4f08115e3a4d1e1056003e433d393e78c20da6af7753409992bb33fb69f04186", size = 139361 }, ] [[package]] name = "botocore" -version = "1.40.55" +version = "1.40.68" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a4/92/dce4842b2e215d213d34b064fcdd13c6a782c43344e77336bcde586e9229/botocore-1.40.55.tar.gz", hash = "sha256:79b6472e2de92b3519d44fc1eec8c5feced7f99a0d10fdea6dc93133426057c1", size = 14446917, upload-time = "2025-10-17T19:34:47.44Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/df/b0300da4cc1fe3e37c8d7a44d835518004454c7d21b579fce9ef2cd691ce/botocore-1.40.68.tar.gz", hash = "sha256:28f41b463d9f012a711ee8b61d4e26cd14ee3b450b816d5dee849aa79155e856", size = 14435596 } wheels = [ - { url = "https://files.pythonhosted.org/packages/21/30/f13bbc36e83b78777ff1abf50a084efcc3336b808e76560d8c5a0c9219e0/botocore-1.40.55-py3-none-any.whl", hash = "sha256:cdc38f7a4ddb30a2cd1cdd4fabde2a5a16e41b5a642292e1c30de5c4e46f5d44", size = 14116107, upload-time = "2025-10-17T19:34:44.398Z" }, + { url = "https://files.pythonhosted.org/packages/7a/72/ac8123169ce48cb2eb593cd4c6a22e66d72bf8dc30fe75191a7669dd036d/botocore-1.40.68-py3-none-any.whl", hash = "sha256:9d514f9c9054e1af055f2cbe9e0d6771d407a600206d45a01b54d5f09538fecb", size = 14097634 }, ] [[package]] name = "cachetools" version = "5.5.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380 } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, + { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080 }, ] [[package]] name = "certifi" version = "2025.10.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519, upload-time = "2025-10-05T04:12:15.808Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286, upload-time = "2025-10-05T04:12:14.03Z" }, + { url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286 }, ] [[package]] @@ -273,83 +282,83 @@ 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/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" }, +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588 } +wheels = [ + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909 }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402 }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328 }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650 }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487 }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726 }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195 }, ] [[package]] name = "charset-normalizer" version = "3.4.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091 }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936 }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180 }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346 }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874 }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076 }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601 }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376 }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825 }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583 }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366 }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300 }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465 }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404 }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092 }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408 }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746 }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889 }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641 }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779 }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035 }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542 }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524 }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395 }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680 }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045 }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687 }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014 }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044 }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940 }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104 }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743 }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402 }, ] [[package]] @@ -359,18 +368,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943 } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, + { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295 }, ] [[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" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 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" }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] [[package]] @@ -380,70 +389,70 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "humanfriendly" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018 }, ] [[package]] name = "coverage" -version = "7.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/38/ee22495420457259d2f3390309505ea98f98a5eed40901cf62196abad006/coverage-7.11.0.tar.gz", hash = "sha256:167bd504ac1ca2af7ff3b81d245dfea0292c5032ebef9d66cc08a7d28c1b8050", size = 811905, upload-time = "2025-10-15T15:15:08.542Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/7f/85e4dfe65e400645464b25c036a26ac226cf3a69d4a50c3934c532491cdd/coverage-7.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cc3f49e65ea6e0d5d9bd60368684fe52a704d46f9e7fc413918f18d046ec40e1", size = 216129, upload-time = "2025-10-15T15:13:25.371Z" }, - { url = "https://files.pythonhosted.org/packages/96/5d/dc5fa98fea3c175caf9d360649cb1aa3715e391ab00dc78c4c66fabd7356/coverage-7.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f39ae2f63f37472c17b4990f794035c9890418b1b8cca75c01193f3c8d3e01be", size = 216380, upload-time = "2025-10-15T15:13:26.976Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f5/3da9cc9596708273385189289c0e4d8197d37a386bdf17619013554b3447/coverage-7.11.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7db53b5cdd2917b6eaadd0b1251cf4e7d96f4a8d24e174bdbdf2f65b5ea7994d", size = 247375, upload-time = "2025-10-15T15:13:28.923Z" }, - { url = "https://files.pythonhosted.org/packages/65/6c/f7f59c342359a235559d2bc76b0c73cfc4bac7d61bb0df210965cb1ecffd/coverage-7.11.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10ad04ac3a122048688387828b4537bc9cf60c0bf4869c1e9989c46e45690b82", size = 249978, upload-time = "2025-10-15T15:13:30.525Z" }, - { url = "https://files.pythonhosted.org/packages/e7/8c/042dede2e23525e863bf1ccd2b92689692a148d8b5fd37c37899ba882645/coverage-7.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4036cc9c7983a2b1f2556d574d2eb2154ac6ed55114761685657e38782b23f52", size = 251253, upload-time = "2025-10-15T15:13:32.174Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a9/3c58df67bfa809a7bddd786356d9c5283e45d693edb5f3f55d0986dd905a/coverage-7.11.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ab934dd13b1c5e94b692b1e01bd87e4488cb746e3a50f798cb9464fd128374b", size = 247591, upload-time = "2025-10-15T15:13:34.147Z" }, - { url = "https://files.pythonhosted.org/packages/26/5b/c7f32efd862ee0477a18c41e4761305de6ddd2d49cdeda0c1116227570fd/coverage-7.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59a6e5a265f7cfc05f76e3bb53eca2e0dfe90f05e07e849930fecd6abb8f40b4", size = 249411, upload-time = "2025-10-15T15:13:38.425Z" }, - { url = "https://files.pythonhosted.org/packages/76/b5/78cb4f1e86c1611431c990423ec0768122905b03837e1b4c6a6f388a858b/coverage-7.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:df01d6c4c81e15a7c88337b795bb7595a8596e92310266b5072c7e301168efbd", size = 247303, upload-time = "2025-10-15T15:13:40.464Z" }, - { url = "https://files.pythonhosted.org/packages/87/c9/23c753a8641a330f45f221286e707c427e46d0ffd1719b080cedc984ec40/coverage-7.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8c934bd088eed6174210942761e38ee81d28c46de0132ebb1801dbe36a390dcc", size = 247157, upload-time = "2025-10-15T15:13:42.087Z" }, - { url = "https://files.pythonhosted.org/packages/c5/42/6e0cc71dc8a464486e944a4fa0d85bdec031cc2969e98ed41532a98336b9/coverage-7.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a03eaf7ec24078ad64a07f02e30060aaf22b91dedf31a6b24d0d98d2bba7f48", size = 248921, upload-time = "2025-10-15T15:13:43.715Z" }, - { url = "https://files.pythonhosted.org/packages/e8/1c/743c2ef665e6858cccb0f84377dfe3a4c25add51e8c7ef19249be92465b6/coverage-7.11.0-cp313-cp313-win32.whl", hash = "sha256:695340f698a5f56f795b2836abe6fb576e7c53d48cd155ad2f80fd24bc63a040", size = 218526, upload-time = "2025-10-15T15:13:45.336Z" }, - { url = "https://files.pythonhosted.org/packages/ff/d5/226daadfd1bf8ddbccefbd3aa3547d7b960fb48e1bdac124e2dd13a2b71a/coverage-7.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:2727d47fce3ee2bac648528e41455d1b0c46395a087a229deac75e9f88ba5a05", size = 219317, upload-time = "2025-10-15T15:13:47.401Z" }, - { url = "https://files.pythonhosted.org/packages/97/54/47db81dcbe571a48a298f206183ba8a7ba79200a37cd0d9f4788fcd2af4a/coverage-7.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:0efa742f431529699712b92ecdf22de8ff198df41e43aeaaadf69973eb93f17a", size = 217948, upload-time = "2025-10-15T15:13:49.096Z" }, - { url = "https://files.pythonhosted.org/packages/e5/8b/cb68425420154e7e2a82fd779a8cc01549b6fa83c2ad3679cd6c088ebd07/coverage-7.11.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:587c38849b853b157706407e9ebdca8fd12f45869edb56defbef2daa5fb0812b", size = 216837, upload-time = "2025-10-15T15:13:51.09Z" }, - { url = "https://files.pythonhosted.org/packages/33/55/9d61b5765a025685e14659c8d07037247de6383c0385757544ffe4606475/coverage-7.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b971bdefdd75096163dd4261c74be813c4508477e39ff7b92191dea19f24cd37", size = 217061, upload-time = "2025-10-15T15:13:52.747Z" }, - { url = "https://files.pythonhosted.org/packages/52/85/292459c9186d70dcec6538f06ea251bc968046922497377bf4a1dc9a71de/coverage-7.11.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:269bfe913b7d5be12ab13a95f3a76da23cf147be7fa043933320ba5625f0a8de", size = 258398, upload-time = "2025-10-15T15:13:54.45Z" }, - { url = "https://files.pythonhosted.org/packages/1f/e2/46edd73fb8bf51446c41148d81944c54ed224854812b6ca549be25113ee0/coverage-7.11.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:dadbcce51a10c07b7c72b0ce4a25e4b6dcb0c0372846afb8e5b6307a121eb99f", size = 260574, upload-time = "2025-10-15T15:13:56.145Z" }, - { url = "https://files.pythonhosted.org/packages/07/5e/1df469a19007ff82e2ca8fe509822820a31e251f80ee7344c34f6cd2ec43/coverage-7.11.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ed43fa22c6436f7957df036331f8fe4efa7af132054e1844918866cd228af6c", size = 262797, upload-time = "2025-10-15T15:13:58.635Z" }, - { url = "https://files.pythonhosted.org/packages/f9/50/de216b31a1434b94d9b34a964c09943c6be45069ec704bfc379d8d89a649/coverage-7.11.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9516add7256b6713ec08359b7b05aeff8850c98d357784c7205b2e60aa2513fa", size = 257361, upload-time = "2025-10-15T15:14:00.409Z" }, - { url = "https://files.pythonhosted.org/packages/82/1e/3f9f8344a48111e152e0fd495b6fff13cc743e771a6050abf1627a7ba918/coverage-7.11.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb92e47c92fcbcdc692f428da67db33337fa213756f7adb6a011f7b5a7a20740", size = 260349, upload-time = "2025-10-15T15:14:02.188Z" }, - { url = "https://files.pythonhosted.org/packages/65/9b/3f52741f9e7d82124272f3070bbe316006a7de1bad1093f88d59bfc6c548/coverage-7.11.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d06f4fc7acf3cabd6d74941d53329e06bab00a8fe10e4df2714f0b134bfc64ef", size = 258114, upload-time = "2025-10-15T15:14:03.907Z" }, - { url = "https://files.pythonhosted.org/packages/0b/8b/918f0e15f0365d50d3986bbd3338ca01178717ac5678301f3f547b6619e6/coverage-7.11.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:6fbcee1a8f056af07ecd344482f711f563a9eb1c2cad192e87df00338ec3cdb0", size = 256723, upload-time = "2025-10-15T15:14:06.324Z" }, - { url = "https://files.pythonhosted.org/packages/44/9e/7776829f82d3cf630878a7965a7d70cc6ca94f22c7d20ec4944f7148cb46/coverage-7.11.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dbbf012be5f32533a490709ad597ad8a8ff80c582a95adc8d62af664e532f9ca", size = 259238, upload-time = "2025-10-15T15:14:08.002Z" }, - { url = "https://files.pythonhosted.org/packages/9a/b8/49cf253e1e7a3bedb85199b201862dd7ca4859f75b6cf25ffa7298aa0760/coverage-7.11.0-cp313-cp313t-win32.whl", hash = "sha256:cee6291bb4fed184f1c2b663606a115c743df98a537c969c3c64b49989da96c2", size = 219180, upload-time = "2025-10-15T15:14:09.786Z" }, - { url = "https://files.pythonhosted.org/packages/ac/e1/1a541703826be7ae2125a0fb7f821af5729d56bb71e946e7b933cc7a89a4/coverage-7.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a386c1061bf98e7ea4758e4313c0ab5ecf57af341ef0f43a0bf26c2477b5c268", size = 220241, upload-time = "2025-10-15T15:14:11.471Z" }, - { url = "https://files.pythonhosted.org/packages/d5/d1/5ee0e0a08621140fd418ec4020f595b4d52d7eb429ae6a0c6542b4ba6f14/coverage-7.11.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f9ea02ef40bb83823b2b04964459d281688fe173e20643870bb5d2edf68bc836", size = 218510, upload-time = "2025-10-15T15:14:13.46Z" }, - { url = "https://files.pythonhosted.org/packages/f4/06/e923830c1985ce808e40a3fa3eb46c13350b3224b7da59757d37b6ce12b8/coverage-7.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c770885b28fb399aaf2a65bbd1c12bf6f307ffd112d6a76c5231a94276f0c497", size = 216110, upload-time = "2025-10-15T15:14:15.157Z" }, - { url = "https://files.pythonhosted.org/packages/42/82/cdeed03bfead45203fb651ed756dfb5266028f5f939e7f06efac4041dad5/coverage-7.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a3d0e2087dba64c86a6b254f43e12d264b636a39e88c5cc0a01a7c71bcfdab7e", size = 216395, upload-time = "2025-10-15T15:14:16.863Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ba/e1c80caffc3199aa699813f73ff097bc2df7b31642bdbc7493600a8f1de5/coverage-7.11.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73feb83bb41c32811973b8565f3705caf01d928d972b72042b44e97c71fd70d1", size = 247433, upload-time = "2025-10-15T15:14:18.589Z" }, - { url = "https://files.pythonhosted.org/packages/80/c0/5b259b029694ce0a5bbc1548834c7ba3db41d3efd3474489d7efce4ceb18/coverage-7.11.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c6f31f281012235ad08f9a560976cc2fc9c95c17604ff3ab20120fe480169bca", size = 249970, upload-time = "2025-10-15T15:14:20.307Z" }, - { url = "https://files.pythonhosted.org/packages/8c/86/171b2b5e1aac7e2fd9b43f7158b987dbeb95f06d1fbecad54ad8163ae3e8/coverage-7.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9570ad567f880ef675673992222746a124b9595506826b210fbe0ce3f0499cd", size = 251324, upload-time = "2025-10-15T15:14:22.419Z" }, - { url = "https://files.pythonhosted.org/packages/1a/7e/7e10414d343385b92024af3932a27a1caf75c6e27ee88ba211221ff1a145/coverage-7.11.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8badf70446042553a773547a61fecaa734b55dc738cacf20c56ab04b77425e43", size = 247445, upload-time = "2025-10-15T15:14:24.205Z" }, - { url = "https://files.pythonhosted.org/packages/c4/3b/e4f966b21f5be8c4bf86ad75ae94efa0de4c99c7bbb8114476323102e345/coverage-7.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a09c1211959903a479e389685b7feb8a17f59ec5a4ef9afde7650bd5eabc2777", size = 249324, upload-time = "2025-10-15T15:14:26.234Z" }, - { url = "https://files.pythonhosted.org/packages/00/a2/8479325576dfcd909244d0df215f077f47437ab852ab778cfa2f8bf4d954/coverage-7.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:5ef83b107f50db3f9ae40f69e34b3bd9337456c5a7fe3461c7abf8b75dd666a2", size = 247261, upload-time = "2025-10-15T15:14:28.42Z" }, - { url = "https://files.pythonhosted.org/packages/7b/d8/3a9e2db19d94d65771d0f2e21a9ea587d11b831332a73622f901157cc24b/coverage-7.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f91f927a3215b8907e214af77200250bb6aae36eca3f760f89780d13e495388d", size = 247092, upload-time = "2025-10-15T15:14:30.784Z" }, - { url = "https://files.pythonhosted.org/packages/b3/b1/bbca3c472544f9e2ad2d5116b2379732957048be4b93a9c543fcd0207e5f/coverage-7.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbcd376716d6b7fbfeedd687a6c4be019c5a5671b35f804ba76a4c0a778cba4", size = 248755, upload-time = "2025-10-15T15:14:32.585Z" }, - { url = "https://files.pythonhosted.org/packages/89/49/638d5a45a6a0f00af53d6b637c87007eb2297042186334e9923a61aa8854/coverage-7.11.0-cp314-cp314-win32.whl", hash = "sha256:bab7ec4bb501743edc63609320aaec8cd9188b396354f482f4de4d40a9d10721", size = 218793, upload-time = "2025-10-15T15:14:34.972Z" }, - { url = "https://files.pythonhosted.org/packages/30/cc/b675a51f2d068adb3cdf3799212c662239b0ca27f4691d1fff81b92ea850/coverage-7.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:3d4ba9a449e9364a936a27322b20d32d8b166553bfe63059bd21527e681e2fad", size = 219587, upload-time = "2025-10-15T15:14:37.047Z" }, - { url = "https://files.pythonhosted.org/packages/93/98/5ac886876026de04f00820e5094fe22166b98dcb8b426bf6827aaf67048c/coverage-7.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:ce37f215223af94ef0f75ac68ea096f9f8e8c8ec7d6e8c346ee45c0d363f0479", size = 218168, upload-time = "2025-10-15T15:14:38.861Z" }, - { url = "https://files.pythonhosted.org/packages/14/d1/b4145d35b3e3ecf4d917e97fc8895bcf027d854879ba401d9ff0f533f997/coverage-7.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:f413ce6e07e0d0dc9c433228727b619871532674b45165abafe201f200cc215f", size = 216850, upload-time = "2025-10-15T15:14:40.651Z" }, - { url = "https://files.pythonhosted.org/packages/ca/d1/7f645fc2eccd318369a8a9948acc447bb7c1ade2911e31d3c5620544c22b/coverage-7.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:05791e528a18f7072bf5998ba772fe29db4da1234c45c2087866b5ba4dea710e", size = 217071, upload-time = "2025-10-15T15:14:42.755Z" }, - { url = "https://files.pythonhosted.org/packages/54/7d/64d124649db2737ceced1dfcbdcb79898d5868d311730f622f8ecae84250/coverage-7.11.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cacb29f420cfeb9283b803263c3b9a068924474ff19ca126ba9103e1278dfa44", size = 258570, upload-time = "2025-10-15T15:14:44.542Z" }, - { url = "https://files.pythonhosted.org/packages/6c/3f/6f5922f80dc6f2d8b2c6f974835c43f53eb4257a7797727e6ca5b7b2ec1f/coverage-7.11.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314c24e700d7027ae3ab0d95fbf8d53544fca1f20345fd30cd219b737c6e58d3", size = 260738, upload-time = "2025-10-15T15:14:46.436Z" }, - { url = "https://files.pythonhosted.org/packages/0e/5f/9e883523c4647c860b3812b417a2017e361eca5b635ee658387dc11b13c1/coverage-7.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:630d0bd7a293ad2fc8b4b94e5758c8b2536fdf36c05f1681270203e463cbfa9b", size = 262994, upload-time = "2025-10-15T15:14:48.3Z" }, - { url = "https://files.pythonhosted.org/packages/07/bb/43b5a8e94c09c8bf51743ffc65c4c841a4ca5d3ed191d0a6919c379a1b83/coverage-7.11.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e89641f5175d65e2dbb44db15fe4ea48fade5d5bbb9868fdc2b4fce22f4a469d", size = 257282, upload-time = "2025-10-15T15:14:50.236Z" }, - { url = "https://files.pythonhosted.org/packages/aa/e5/0ead8af411411330b928733e1d201384b39251a5f043c1612970310e8283/coverage-7.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c9f08ea03114a637dab06cedb2e914da9dc67fa52c6015c018ff43fdde25b9c2", size = 260430, upload-time = "2025-10-15T15:14:52.413Z" }, - { url = "https://files.pythonhosted.org/packages/ae/66/03dd8bb0ba5b971620dcaac145461950f6d8204953e535d2b20c6b65d729/coverage-7.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce9f3bde4e9b031eaf1eb61df95c1401427029ea1bfddb8621c1161dcb0fa02e", size = 258190, upload-time = "2025-10-15T15:14:54.268Z" }, - { url = "https://files.pythonhosted.org/packages/45/ae/28a9cce40bf3174426cb2f7e71ee172d98e7f6446dff936a7ccecee34b14/coverage-7.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:e4dc07e95495923d6fd4d6c27bf70769425b71c89053083843fd78f378558996", size = 256658, upload-time = "2025-10-15T15:14:56.436Z" }, - { url = "https://files.pythonhosted.org/packages/5c/7c/3a44234a8599513684bfc8684878fd7b126c2760f79712bb78c56f19efc4/coverage-7.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:424538266794db2861db4922b05d729ade0940ee69dcf0591ce8f69784db0e11", size = 259342, upload-time = "2025-10-15T15:14:58.538Z" }, - { url = "https://files.pythonhosted.org/packages/e1/e6/0108519cba871af0351725ebdb8660fd7a0fe2ba3850d56d32490c7d9b4b/coverage-7.11.0-cp314-cp314t-win32.whl", hash = "sha256:4c1eeb3fb8eb9e0190bebafd0462936f75717687117339f708f395fe455acc73", size = 219568, upload-time = "2025-10-15T15:15:00.382Z" }, - { url = "https://files.pythonhosted.org/packages/c9/76/44ba876e0942b4e62fdde23ccb029ddb16d19ba1bef081edd00857ba0b16/coverage-7.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b56efee146c98dbf2cf5cffc61b9829d1e94442df4d7398b26892a53992d3547", size = 220687, upload-time = "2025-10-15T15:15:02.322Z" }, - { url = "https://files.pythonhosted.org/packages/b9/0c/0df55ecb20d0d0ed5c322e10a441775e1a3a5d78c60f0c4e1abfe6fcf949/coverage-7.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b5c2705afa83f49bd91962a4094b6b082f94aef7626365ab3f8f4bd159c5acf3", size = 218711, upload-time = "2025-10-15T15:15:04.575Z" }, - { url = "https://files.pythonhosted.org/packages/5f/04/642c1d8a448ae5ea1369eac8495740a79eb4e581a9fb0cbdce56bbf56da1/coverage-7.11.0-py3-none-any.whl", hash = "sha256:4b7589765348d78fb4e5fb6ea35d07564e387da2fc5efff62e0222971f155f68", size = 207761, upload-time = "2025-10-15T15:15:06.439Z" }, +version = "7.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/89/12/3e2d2ec71796e0913178478e693a06af6a3bc9f7f9cb899bf85a426d8370/coverage-7.11.1.tar.gz", hash = "sha256:b4b3a072559578129a9e863082a2972a2abd8975bc0e2ec57da96afcd6580a8a", size = 814037 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/01/0c50c318f5e8f1a482da05d788d0ff06137803ed8fface4a1ba51e04b3ad/coverage-7.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:da9930594ca99d66eb6f613d7beba850db2f8dfa86810ee35ae24e4d5f2bb97d", size = 216920 }, + { url = "https://files.pythonhosted.org/packages/20/11/9f038e6c2baea968c377ab355b0d1d0a46b5f38985691bf51164e1b78c1f/coverage-7.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc47a280dc014220b0fc6e5f55082a3f51854faf08fd9635b8a4f341c46c77d3", size = 217301 }, + { url = "https://files.pythonhosted.org/packages/68/cd/9dcf93d81d0cddaa0bba90c3b4580e6f1ddf833918b816930d250cc553a4/coverage-7.11.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:74003324321bbf130939146886eddf92e48e616b5910215e79dea6edeb8ee7c8", size = 248277 }, + { url = "https://files.pythonhosted.org/packages/11/f5/b2c7c494046c9c783d3cac4c812fc24d6104dd36a7a598e7dd6fea3e7927/coverage-7.11.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:211f7996265daab60a8249af4ca6641b3080769cbedcffc42cc4841118f3a305", size = 250871 }, + { url = "https://files.pythonhosted.org/packages/a5/5a/b359649566954498aa17d7c98093182576d9e435ceb4ea917b3b48d56f86/coverage-7.11.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70619d194d8fea0cb028cb6bb9c85b519c7509c1d1feef1eea635183bc8ecd27", size = 252115 }, + { url = "https://files.pythonhosted.org/packages/f3/17/3cef1ede3739622950f0737605353b797ec564e70c9d254521b10f4b03ba/coverage-7.11.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e0208bb59d441cfa3321569040f8e455f9261256e0df776c5462a1e5a9b31e13", size = 248442 }, + { url = "https://files.pythonhosted.org/packages/5f/63/d5854c47ae42d9d18855329db6bc528f5b7f4f874257edb00cf8b483f9f8/coverage-7.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:545714d8765bda1c51f8b1c96e0b497886a054471c68211e76ef49dd1468587d", size = 250253 }, + { url = "https://files.pythonhosted.org/packages/48/e8/c7706f8a5358a59c18b489e7e19e83d6161b7c8bc60771f95920570c94a8/coverage-7.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d0a2b02c1e20158dd405054bcca87f91fd5b7605626aee87150819ea616edd67", size = 248217 }, + { url = "https://files.pythonhosted.org/packages/5b/c9/a2136dfb168eb09e2f6d9d6b6c986243fdc0b3866a9376adb263d3c3378b/coverage-7.11.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0f4aa986a4308a458e0fb572faa3eb3db2ea7ce294604064b25ab32b435a468", size = 248040 }, + { url = "https://files.pythonhosted.org/packages/18/9a/a63991c0608ddc6adf65e6f43124951aaf36bd79f41937b028120b8268ea/coverage-7.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d51cc6687e8bbfd1e041f52baed0f979cd592242cf50bf18399a7e03afc82d88", size = 249801 }, + { url = "https://files.pythonhosted.org/packages/84/19/947acf7c0c6e90e4ec3abf474133ed36d94407d07e36eafdfd3acb59fee9/coverage-7.11.1-cp313-cp313-win32.whl", hash = "sha256:1b3067db3afe6deeca2b2c9f0ec23820d5f1bd152827acfadf24de145dfc5f66", size = 219430 }, + { url = "https://files.pythonhosted.org/packages/35/54/36fef7afb3884450c7b6d494fcabe2fab7c669d547c800ca30f41c1dc212/coverage-7.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:39a4c44b0cd40e3c9d89b2b7303ebd6ab9ae8a63f9e9a8c4d65a181a0b33aebe", size = 220239 }, + { url = "https://files.pythonhosted.org/packages/d3/dc/7d38bb99e8e69200b7dd5de15507226bd90eac102dfc7cc891b9934cdc76/coverage-7.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:a2e3560bf82fa8169a577e054cbbc29888699526063fee26ea59ea2627fd6e73", size = 218868 }, + { url = "https://files.pythonhosted.org/packages/36/c6/d1ff54fbd6bcad42dbcfd13b417e636ef84aae194353b1ef3361700f2525/coverage-7.11.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47a4f362a10285897ab3aa7a4b37d28213a4f2626823923613d6d7a3584dd79a", size = 217615 }, + { url = "https://files.pythonhosted.org/packages/73/f9/6ed59e7cf1488d6f975e5b14ef836f5e537913523e92175135f8518a83ce/coverage-7.11.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0df35fa7419ef571db9dacd50b0517bc54dbfe37eb94043b5fc3540bff276acd", size = 217960 }, + { url = "https://files.pythonhosted.org/packages/c4/74/2dab1dc2ebe16f074f80ae483b0f45faf278d102be703ac01b32cd85b6c3/coverage-7.11.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e1a2c621d341c9d56f7917e56fbb56be4f73fe0d0e8dae28352fb095060fd467", size = 259262 }, + { url = "https://files.pythonhosted.org/packages/15/49/eccfe039663e29a50a54b0c2c8d076acd174d7ac50d018ef8a5b1c37c8dc/coverage-7.11.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c354b111be9b2234d9573d75dd30ca4e414b7659c730e477e89be4f620b3fb5", size = 261326 }, + { url = "https://files.pythonhosted.org/packages/f0/bb/2b829aa23fd5ee8318e33cc02a606eb09900921291497963adc3f06af8bb/coverage-7.11.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4589bd44698728f600233fb2881014c9b8ec86637ef454c00939e779661dbe7e", size = 263758 }, + { url = "https://files.pythonhosted.org/packages/ac/03/d44c3d70e5da275caf2cad2071da6b425412fbcb1d1d5a81f1f89b45e3f1/coverage-7.11.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c6956fc8754f2309131230272a7213a483a32ecbe29e2b9316d808a28f2f8ea1", size = 258444 }, + { url = "https://files.pythonhosted.org/packages/a9/c1/cf61d9f46ae088774c65dd3387a15dfbc72de90c1f6e105025e9eda19b42/coverage-7.11.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63926a97ed89dc6a087369b92dcb8b9a94cead46c08b33a7f1f4818cd8b6a3c3", size = 261335 }, + { url = "https://files.pythonhosted.org/packages/95/9a/b3299bb14f11f2364d78a2b9704491b15395e757af6116694731ce4e5834/coverage-7.11.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f5311ba00c53a7fb2b293fdc1f478b7286fe2a845a7ba9cda053f6e98178f0b4", size = 258951 }, + { url = "https://files.pythonhosted.org/packages/3f/a3/73cb2763e59f14ba6d8d6444b1f640a9be2242bfb59b7e50581c695db7ff/coverage-7.11.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:31bf5ffad84c974f9e72ac53493350f36b6fa396109159ec704210698f12860b", size = 257840 }, + { url = "https://files.pythonhosted.org/packages/85/db/482e72589a952027e238ffa3a15f192c552e0685fd0c5220ad05b5f17d56/coverage-7.11.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:227ee59fbc4a8c57a7383a1d7af6ca94a78ae3beee4045f38684548a8479a65b", size = 260040 }, + { url = "https://files.pythonhosted.org/packages/18/a1/b931d3ee099c2dca8e9ea56c07ae84c0f91562f7bbbcccab8c91b3474ef1/coverage-7.11.1-cp313-cp313t-win32.whl", hash = "sha256:a447d97b3ce680bb1da2e6bd822ebb71be6a1fb77ce2c2ad2fe4bd8aacec3058", size = 220102 }, + { url = "https://files.pythonhosted.org/packages/9a/53/b553b7bfa6207def4918f0cb72884c844fa4c3f1566e58fbb4f34e54cdc5/coverage-7.11.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d6d11180437c67bde2248563a42b8e5bbf85c8df78fae13bf818ad17bfb15f02", size = 221166 }, + { url = "https://files.pythonhosted.org/packages/6b/45/1c1d58b3ed585598764bd2fe41fcf60ccafe15973ad621c322ba52e22d32/coverage-7.11.1-cp313-cp313t-win_arm64.whl", hash = "sha256:1e19a4c43d612760c6f7190411fb157e2d8a6dde00c91b941d43203bd3b17f6f", size = 219439 }, + { url = "https://files.pythonhosted.org/packages/d9/c2/ac2c3417eaa4de1361036ebbc7da664242b274b2e00c4b4a1cfc7b29920b/coverage-7.11.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0305463c45c5f21f0396cd5028de92b1f1387e2e0756a85dd3147daa49f7a674", size = 216967 }, + { url = "https://files.pythonhosted.org/packages/5e/a3/afef455d03c468ee303f9df9a6f407e8bea64cd576fca914ff888faf52ca/coverage-7.11.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fa4d468d5efa1eb6e3062be8bd5f45cbf28257a37b71b969a8c1da2652dfec77", size = 217298 }, + { url = "https://files.pythonhosted.org/packages/9d/59/6e2fb3fb58637001132dc32228b4fb5b332d75d12f1353cb00fe084ee0ba/coverage-7.11.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d2b2f5fc8fe383cbf2d5c77d6c4b2632ede553bc0afd0cdc910fa5390046c290", size = 248337 }, + { url = "https://files.pythonhosted.org/packages/1d/5e/ce442bab963e3388658da8bde6ddbd0a15beda230afafaa25e3c487dc391/coverage-7.11.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bde6488c1ad509f4fb1a4f9960fd003d5a94adef61e226246f9699befbab3276", size = 250853 }, + { url = "https://files.pythonhosted.org/packages/d1/2f/43f94557924ca9b64e09f1c3876da4eec44a05a41e27b8a639d899716c0e/coverage-7.11.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a69e0d6fa0b920fe6706a898c52955ec5bcfa7e45868215159f45fd87ea6da7c", size = 252190 }, + { url = "https://files.pythonhosted.org/packages/8c/fa/a04e769b92bc5628d4bd909dcc3c8219efe5e49f462e29adc43e198ecfde/coverage-7.11.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:976e51e4a549b80e4639eda3a53e95013a14ff6ad69bb58ed604d34deb0e774c", size = 248335 }, + { url = "https://files.pythonhosted.org/packages/99/d0/b98ab5d2abe425c71117a7c690ead697a0b32b83256bf0f566c726b7f77b/coverage-7.11.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d61fcc4d384c82971a3d9cf00d0872881f9ded19404c714d6079b7a4547e2955", size = 250209 }, + { url = "https://files.pythonhosted.org/packages/9c/3f/b9c4fbd2e6d1b64098f99fb68df7f7c1b3e0a0968d24025adb24f359cdec/coverage-7.11.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:284c5df762b533fae3ebd764e3b81c20c1c9648d93ef34469759cb4e3dfe13d0", size = 248163 }, + { url = "https://files.pythonhosted.org/packages/08/fc/3e4d54fb6368b0628019eefd897fc271badbd025410fd5421a65fb58758f/coverage-7.11.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:bab32cb1d4ad2ac6dcc4e17eee5fa136c2a1d14ae914e4bce6c8b78273aece3c", size = 247983 }, + { url = "https://files.pythonhosted.org/packages/b9/4a/a5700764a12e932b35afdddb2f59adbca289c1689455d06437f609f3ef35/coverage-7.11.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:36f2fed9ce392ca450fb4e283900d0b41f05c8c5db674d200f471498be3ce747", size = 249646 }, + { url = "https://files.pythonhosted.org/packages/0e/2c/45ed33d9e80a1cc9b44b4bd535d44c154d3204671c65abd90ec1e99522a2/coverage-7.11.1-cp314-cp314-win32.whl", hash = "sha256:853136cecb92a5ba1cc8f61ec6ffa62ca3c88b4b386a6c835f8b833924f9a8c5", size = 219700 }, + { url = "https://files.pythonhosted.org/packages/90/d7/5845597360f6434af1290118ebe114642865f45ce47e7e822d9c07b371be/coverage-7.11.1-cp314-cp314-win_amd64.whl", hash = "sha256:77443d39143e20927259a61da0c95d55ffc31cf43086b8f0f11a92da5260d592", size = 220516 }, + { url = "https://files.pythonhosted.org/packages/ae/d0/d311a06f9cf7a48a98ffcfd0c57db0dcab6da46e75c439286a50dc648161/coverage-7.11.1-cp314-cp314-win_arm64.whl", hash = "sha256:829acb88fa47591a64bf5197e96a931ce9d4b3634c7f81a224ba3319623cdf6c", size = 219091 }, + { url = "https://files.pythonhosted.org/packages/a7/3d/c6a84da4fa9b840933045b19dd19d17b892f3f2dd1612903260291416dba/coverage-7.11.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2ad1fe321d9522ea14399de83e75a11fb6a8887930c3679feb383301c28070d9", size = 217700 }, + { url = "https://files.pythonhosted.org/packages/94/10/a4fc5022017dd7ac682dc423849c241dfbdad31734b8f96060d84e70b587/coverage-7.11.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f69c332f0c3d1357c74decc9b1843fcd428cf9221bf196a20ad22aa1db3e1b6c", size = 217968 }, + { url = "https://files.pythonhosted.org/packages/59/2d/a554cd98924d296de5816413280ac3b09e42a05fb248d66f8d474d321938/coverage-7.11.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:576baeea4eebde684bf6c91c01e97171c8015765c8b2cfd4022a42b899897811", size = 259334 }, + { url = "https://files.pythonhosted.org/packages/05/98/d484cb659ec33958ca96b6f03438f56edc23b239d1ad0417b7a97fc1848a/coverage-7.11.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:28ad84c694fa86084cfd3c1eab4149844b8cb95bd8e5cbfc4a647f3ee2cce2b3", size = 261445 }, + { url = "https://files.pythonhosted.org/packages/f3/fa/920cba122cc28f4557c0507f8bd7c6e527ebcc537d0309186f66464a8fd9/coverage-7.11.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b1043ff958f09fc3f552c014d599f3c6b7088ba97d7bc1bd1cce8603cd75b520", size = 263858 }, + { url = "https://files.pythonhosted.org/packages/2a/a0/036397bdbee0f3bd46c2e26fdfbb1a61b2140bf9059240c37b61149047fa/coverage-7.11.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c6681add5060c2742dafcf29826dff1ff8eef889a3b03390daeed84361c428bd", size = 258381 }, + { url = "https://files.pythonhosted.org/packages/b6/61/2533926eb8990f182eb287f4873216c8ca530cc47241144aabf46fe80abe/coverage-7.11.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:773419b225ec9a75caa1e941dd0c83a91b92c2b525269e44e6ee3e4c630607db", size = 261321 }, + { url = "https://files.pythonhosted.org/packages/32/6e/618f7e203a998e4f6b8a0fa395744a416ad2adbcdc3735bc19466456718a/coverage-7.11.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a9cb272a0e0157dbb9b2fd0b201b759bd378a1a6138a16536c025c2ce4f7643b", size = 258933 }, + { url = "https://files.pythonhosted.org/packages/22/40/6b1c27f772cb08a14a338647ead1254a57ee9dabbb4cacbc15df7f278741/coverage-7.11.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:e09adb2a7811dc75998eef68f47599cf699e2b62eed09c9fefaeb290b3920f34", size = 257756 }, + { url = "https://files.pythonhosted.org/packages/73/07/f9cd12f71307a785ea15b009c8d8cc2543e4a867bd04b8673843970b6b43/coverage-7.11.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1335fa8c2a2fea49924d97e1e3500cfe8d7c849f5369f26bb7559ad4259ccfab", size = 260086 }, + { url = "https://files.pythonhosted.org/packages/34/02/31c5394f6f5d72a466966bcfdb61ce5a19862d452816d6ffcbb44add16ee/coverage-7.11.1-cp314-cp314t-win32.whl", hash = "sha256:4782d71d2a4fa7cef95e853b7097c8bbead4dbd0e6f9c7152a6b11a194b794db", size = 220483 }, + { url = "https://files.pythonhosted.org/packages/7f/96/81e1ef5fbfd5090113a96e823dbe055e4c58d96ca73b1fb0ad9d26f9ec36/coverage-7.11.1-cp314-cp314t-win_amd64.whl", hash = "sha256:939f45e66eceb63c75e8eb8fc58bb7077c00f1a41b0e15c6ef02334a933cfe93", size = 221592 }, + { url = "https://files.pythonhosted.org/packages/38/7a/a5d050de44951ac453a2046a0f3fb5471a4a557f0c914d00db27d543d94c/coverage-7.11.1-cp314-cp314t-win_arm64.whl", hash = "sha256:01c575bdbef35e3f023b50a146e9a75c53816e4f2569109458155cd2315f87d9", size = 219627 }, + { url = "https://files.pythonhosted.org/packages/76/32/bd9f48c28e23b2f08946f8e83983617b00619f5538dbd7e1045fa7e88c00/coverage-7.11.1-py3-none-any.whl", hash = "sha256:0fa848acb5f1da24765cee840e1afe9232ac98a8f9431c6112c15b34e880b9e8", size = 208689 }, ] [[package]] @@ -453,99 +462,99 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" }, - { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" }, - { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" }, - { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" }, - { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" }, - { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" }, - { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" }, - { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" }, - { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" }, - { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" }, - { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" }, - { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" }, - { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" }, - { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" }, - { url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" }, - { url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" }, - { url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" }, - { url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" }, - { url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" }, - { url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" }, - { url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" }, - { url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" }, - { url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" }, - { url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" }, - { url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" }, - { url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" }, - { url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" }, - { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" }, - { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" }, - { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" }, - { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" }, - { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" }, - { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" }, - { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" }, - { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" }, - { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" }, - { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" }, - { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" }, - { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" }, - { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004 }, + { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667 }, + { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807 }, + { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615 }, + { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800 }, + { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707 }, + { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541 }, + { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464 }, + { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838 }, + { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596 }, + { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782 }, + { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381 }, + { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988 }, + { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451 }, + { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007 }, + { url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012 }, + { url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728 }, + { url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078 }, + { url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460 }, + { url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237 }, + { url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344 }, + { url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564 }, + { url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415 }, + { url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457 }, + { url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074 }, + { url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569 }, + { url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941 }, + { url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339 }, + { url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315 }, + { url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331 }, + { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248 }, + { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089 }, + { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029 }, + { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222 }, + { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280 }, + { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958 }, + { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714 }, + { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970 }, + { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236 }, + { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642 }, + { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126 }, + { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573 }, + { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695 }, + { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720 }, + { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740 }, ] [[package]] name = "dnspython" version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094 }, ] [[package]] name = "docopt" version = "0.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901 } [[package]] name = "dotmap" version = "1.3.30" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/68/c186606e4f2bf731abd18044ea201e70c3c244bf468f41368820d197fca5/dotmap-1.3.30.tar.gz", hash = "sha256:5821a7933f075fb47563417c0e92e0b7c031158b4c9a6a7e56163479b658b368", size = 12391, upload-time = "2022-04-06T16:26:49.211Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/68/c186606e4f2bf731abd18044ea201e70c3c244bf468f41368820d197fca5/dotmap-1.3.30.tar.gz", hash = "sha256:5821a7933f075fb47563417c0e92e0b7c031158b4c9a6a7e56163479b658b368", size = 12391 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/f9/976d6813c160d6c89196d81e9466dca1503d20e609d8751f3536daf37ec6/dotmap-1.3.30-py3-none-any.whl", hash = "sha256:bd9fa15286ea2ad899a4d1dc2445ed85a1ae884a42effb87c89a6ecce71243c6", size = 11464, upload-time = "2022-04-06T16:26:47.103Z" }, + { url = "https://files.pythonhosted.org/packages/4d/f9/976d6813c160d6c89196d81e9466dca1503d20e609d8751f3536daf37ec6/dotmap-1.3.30-py3-none-any.whl", hash = "sha256:bd9fa15286ea2ad899a4d1dc2445ed85a1ae884a42effb87c89a6ecce71243c6", size = 11464 }, ] [[package]] name = "email-validator" -version = "2.2.0" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dnspython" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967, upload-time = "2024-06-20T11:30:30.034Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521, upload-time = "2024-06-20T11:30:28.248Z" }, + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604 }, ] [[package]] name = "et-xmlfile" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059 }, ] [[package]] @@ -555,35 +564,36 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "faker" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/98/75cacae9945f67cfe323829fc2ac451f64517a8a330b572a06a323997065/factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03", size = 164146, upload-time = "2025-02-03T09:49:04.433Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/98/75cacae9945f67cfe323829fc2ac451f64517a8a330b572a06a323997065/factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03", size = 164146 } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/8d/2bc5f5546ff2ccb3f7de06742853483ab75bf74f36a92254702f8baecc79/factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc", size = 37036, upload-time = "2025-02-03T09:49:01.659Z" }, + { url = "https://files.pythonhosted.org/packages/27/8d/2bc5f5546ff2ccb3f7de06742853483ab75bf74f36a92254702f8baecc79/factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc", size = 37036 }, ] [[package]] name = "faker" -version = "37.11.0" +version = "37.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/4b/ca43f6bbcef63deb8ac01201af306388670a172587169aab3b192f7490f0/faker-37.11.0.tar.gz", hash = "sha256:22969803849ba0618be8eee2dd01d0d9e2cd3b75e6ff1a291fa9abcdb34da5e6", size = 1935301, upload-time = "2025-10-07T14:49:01.481Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/84/e95acaa848b855e15c83331d0401ee5f84b2f60889255c2e055cb4fb6bdf/faker-37.12.0.tar.gz", hash = "sha256:7505e59a7e02fa9010f06c3e1e92f8250d4cfbb30632296140c2d6dbef09b0fa", size = 1935741 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/46/8f4097b55e43af39e8e71e1f7aec59ff7398bca54d975c30889bc844719d/faker-37.11.0-py3-none-any.whl", hash = "sha256:1508d2da94dfd1e0087b36f386126d84f8583b3de19ac18e392a2831a6676c57", size = 1975525, upload-time = "2025-10-07T14:48:58.29Z" }, + { url = "https://files.pythonhosted.org/packages/8e/98/2c050dec90e295a524c9b65c4cb9e7c302386a296b2938710448cbd267d5/faker-37.12.0-py3-none-any.whl", hash = "sha256:afe7ccc038da92f2fbae30d8e16d19d91e92e242f8401ce9caf44de892bab4c4", size = 1975461 }, ] [[package]] name = "fastapi" -version = "0.119.1" +version = "0.121.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "annotated-doc" }, { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/f4/152127681182e6413e7a89684c434e19e7414ed7ac0c632999c3c6980640/fastapi-0.119.1.tar.gz", hash = "sha256:a5e3426edce3fe221af4e1992c6d79011b247e3b03cc57999d697fe76cbf8ae0", size = 338616, upload-time = "2025-10-20T11:30:27.734Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/e3/77a2df0946703973b9905fd0cde6172c15e0781984320123b4f5079e7113/fastapi-0.121.0.tar.gz", hash = "sha256:06663356a0b1ee93e875bbf05a31fb22314f5bed455afaaad2b2dad7f26e98fa", size = 342412 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/26/e6d959b4ac959fdb3e9c4154656fc160794db6af8e64673d52759456bf07/fastapi-0.119.1-py3-none-any.whl", hash = "sha256:0b8c2a2cce853216e150e9bd4faaed88227f8eb37de21cb200771f491586a27f", size = 108123, upload-time = "2025-10-20T11:30:26.185Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/42277afc1ba1a18f8358561eee40785d27becab8f80a1f945c0a3051c6eb/fastapi-0.121.0-py3-none-any.whl", hash = "sha256:8bdf1b15a55f4e4b0d6201033da9109ea15632cb76cf156e7b8b4019f2172106", size = 109183 }, ] [package.optional-dependencies] @@ -605,9 +615,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/13/11e43d630be84e51ba5510a6da6a11eb93b44b72caa796137c5dddda937b/fastapi_cli-0.0.14.tar.gz", hash = "sha256:ddfb5de0a67f77a8b3271af1460489bd4d7f4add73d11fbfac613827b0275274", size = 17994, upload-time = "2025-10-20T16:33:21.054Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/13/11e43d630be84e51ba5510a6da6a11eb93b44b72caa796137c5dddda937b/fastapi_cli-0.0.14.tar.gz", hash = "sha256:ddfb5de0a67f77a8b3271af1460489bd4d7f4add73d11fbfac613827b0275274", size = 17994 } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/e8/bc8bbfd93dcc8e347ce98a3e654fb0d2e5f2739afb46b98f41a30c339269/fastapi_cli-0.0.14-py3-none-any.whl", hash = "sha256:e66b9ad499ee77a4e6007545cde6de1459b7f21df199d7f29aad2adaab168eca", size = 11151, upload-time = "2025-10-20T16:33:19.318Z" }, + { url = "https://files.pythonhosted.org/packages/40/e8/bc8bbfd93dcc8e347ce98a3e654fb0d2e5f2739afb46b98f41a30c339269/fastapi_cli-0.0.14-py3-none-any.whl", hash = "sha256:e66b9ad499ee77a4e6007545cde6de1459b7f21df199d7f29aad2adaab168eca", size = 11151 }, ] [package.optional-dependencies] @@ -629,9 +639,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f9/48/0f14d8555b750dc8c04382804e4214f1d7f55298127f3a0237ba566e69dd/fastapi_cloud_cli-0.3.1.tar.gz", hash = "sha256:8c7226c36e92e92d0c89827e8f56dbf164ab2de4444bd33aa26b6c3f7675db69", size = 24080, upload-time = "2025-10-09T11:32:58.174Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/48/0f14d8555b750dc8c04382804e4214f1d7f55298127f3a0237ba566e69dd/fastapi_cloud_cli-0.3.1.tar.gz", hash = "sha256:8c7226c36e92e92d0c89827e8f56dbf164ab2de4444bd33aa26b6c3f7675db69", size = 24080 } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/79/7f5a5e5513e6a737e5fb089d9c59c74d4d24dc24d581d3aa519b326bedda/fastapi_cloud_cli-0.3.1-py3-none-any.whl", hash = "sha256:7d1a98a77791a9d0757886b2ffbf11bcc6b3be93210dd15064be10b216bf7e00", size = 19711, upload-time = "2025-10-09T11:32:57.118Z" }, + { url = "https://files.pythonhosted.org/packages/68/79/7f5a5e5513e6a737e5fb089d9c59c74d4d24dc24d581d3aa519b326bedda/fastapi_cloud_cli-0.3.1-py3-none-any.whl", hash = "sha256:7d1a98a77791a9d0757886b2ffbf11bcc6b3be93210dd15064be10b216bf7e00", size = 19711 }, ] [[package]] @@ -642,41 +652,40 @@ dependencies = [ { name = "fastapi" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/ed/c36cfcd849519fd2d23051ad81a91fc5e8cfa7109496fc8a10ad565a5fe9/fastapi_filter-2.0.1.tar.gz", hash = "sha256:cffda370097af7e404f1eb188aca58b199084bfaf7cec881e40b404adf12566e", size = 9857, upload-time = "2024-12-07T17:30:06.343Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/ed/c36cfcd849519fd2d23051ad81a91fc5e8cfa7109496fc8a10ad565a5fe9/fastapi_filter-2.0.1.tar.gz", hash = "sha256:cffda370097af7e404f1eb188aca58b199084bfaf7cec881e40b404adf12566e", size = 9857 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/88/afc022ad64d12f730141fc50758ecf9d60de5fed11335dc16e3127617f05/fastapi_filter-2.0.1-py3-none-any.whl", hash = "sha256:711d48707ec62f7c9e12a7713fc0f6a99858a9e3741b4d108102d5599e77197d", size = 11586, upload-time = "2024-12-07T17:30:05.375Z" }, + { url = "https://files.pythonhosted.org/packages/5e/88/afc022ad64d12f730141fc50758ecf9d60de5fed11335dc16e3127617f05/fastapi_filter-2.0.1-py3-none-any.whl", hash = "sha256:711d48707ec62f7c9e12a7713fc0f6a99858a9e3741b4d108102d5599e77197d", size = 11586 }, ] [[package]] name = "fastapi-mail" -version = "1.5.2" -source = { registry = "https://pypi.org/simple" } +version = "1.2.6" +source = { git = "https://github.com/simonvanlierde/fastapi-mail?rev=f32147ec1a450ed22262913c5ac7ec3b67dd0117#f32147ec1a450ed22262913c5ac7ec3b67dd0117" } dependencies = [ { name = "aiosmtplib" }, { name = "blinker" }, + { name = "cryptography" }, { name = "email-validator" }, { name = "jinja2" }, { name = "pydantic" }, { name = "pydantic-settings" }, + { name = "regex" }, { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/65/0c/05837963c44ce15e4c81e95bb8bb8a2910fddd60a2f41ac5c015c068c53e/fastapi_mail-1.5.2.tar.gz", hash = "sha256:c83b96f1a030db754e83c64d8687b62b3d4f847d25b5adb00f30d5765ff9825a", size = 13312, upload-time = "2025-10-16T11:13:40.484Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/da/30e1a709d85a3132220538714af09b9befad9e81703021dde698c884187a/fastapi_mail-1.5.2-py3-none-any.whl", hash = "sha256:158ecf49075430cb6a5483f557a8f45b987e31f2105a77b3239933b0bacb03e5", size = 15153, upload-time = "2025-10-16T11:13:39.529Z" }, + { name = "typing-extensions" }, ] [[package]] name = "fastapi-pagination" -version = "0.14.3" +version = "0.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastapi" }, { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/df/b8a227a621713ed0133a737dee91066beb09e8769ff875225319da4a3a26/fastapi_pagination-0.14.3.tar.gz", hash = "sha256:be8e81e21235c0758cbdd2f0e597c65bcb82a85062e2b99a9474418d23006791", size = 568147, upload-time = "2025-10-08T10:58:01.833Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/db/8a3d097c491ad873574bd7295834ef89e16263ae9104855bbb5ee6d46e47/fastapi_pagination-0.15.0.tar.gz", hash = "sha256:11fe39cbe181ed3c18919b90faf6bfcbe40cb596aa9c52a98bbce85111a29a4f", size = 557472 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/6a/0b6804e1c20013855379fe58e02206e9cc7f7131653d8daad1af6be67851/fastapi_pagination-0.14.3-py3-none-any.whl", hash = "sha256:e87350b64010fd3b2df840218b1f65a21eec6078238cd3a1794c2468a03ea45f", size = 52559, upload-time = "2025-10-08T10:58:00.428Z" }, + { url = "https://files.pythonhosted.org/packages/68/91/b835e07234170ba85473227aa107bcf1dc616ff6cb643c0bd9b8225a55f1/fastapi_pagination-0.15.0-py3-none-any.whl", hash = "sha256:ffef937e78903fcb6f356b8407ec1fb0620a06675087fa7d0c4e537a60aa0447", size = 52292 }, ] [[package]] @@ -686,14 +695,14 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "boto3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/8a/e56d4ade659994c2989091b96642f10554ec3914a40de56a556ffdbbcd26/fastapi_storages-0.3.0.tar.gz", hash = "sha256:f784335fff9cd163b783e842da04c6d9ed1b306fce8995fda109b170d6d453df", size = 6706, upload-time = "2024-02-15T15:14:26.431Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/8a/e56d4ade659994c2989091b96642f10554ec3914a40de56a556ffdbbcd26/fastapi_storages-0.3.0.tar.gz", hash = "sha256:f784335fff9cd163b783e842da04c6d9ed1b306fce8995fda109b170d6d453df", size = 6706 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/b5/3fb94b4f329fb2f83ffa54e6941b769deaaaa77f63a9fe70905219dd7339/fastapi_storages-0.3.0-py3-none-any.whl", hash = "sha256:91adb41a80fdef2a84c0f8244c27ade7ff8bd5db9b7fa95c496c06c03e192477", size = 9725, upload-time = "2024-02-15T15:14:27.567Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b5/3fb94b4f329fb2f83ffa54e6941b769deaaaa77f63a9fe70905219dd7339/fastapi_storages-0.3.0-py3-none-any.whl", hash = "sha256:91adb41a80fdef2a84c0f8244c27ade7ff8bd5db9b7fa95c496c06c03e192477", size = 9725 }, ] [[package]] name = "fastapi-users" -version = "14.0.1" +version = "15.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "email-validator" }, @@ -703,9 +712,9 @@ dependencies = [ { name = "pyjwt", extra = ["crypto"] }, { name = "python-multipart" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/26/7fe4e6a4f60d9cde2b95f58ba45ff03219b62bd03bea75d914b723ecfa2a/fastapi_users-14.0.1.tar.gz", hash = "sha256:8c032b3a75c6fb2b1f5eab8ffce5321176e9916efe1fe93e7c15ee55f0b02236", size = 120315, upload-time = "2025-01-04T13:20:05.95Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/ea/6c0ba809f29d22ad53ab25bbae4408f00b0a3375b71bd21c39dcc3a16044/fastapi_users-15.0.1.tar.gz", hash = "sha256:c822755c1288740a919636d3463797e54df91b53c1c6f4917693d499867d21a7", size = 120916 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/52/2821d3e95a92567d38f98a33d1ef89302aa3448866bf45ff19a48a5f28f8/fastapi_users-14.0.1-py3-none-any.whl", hash = "sha256:074df59676dccf79412d2880bdcb661ab1fabc2ecec1f043b4e6a23be97ed9e1", size = 38717, upload-time = "2025-01-04T13:20:04.441Z" }, + { url = "https://files.pythonhosted.org/packages/59/7f/1bff91a48e755e659d0505f597a8e010ec92059f2219a838fd15887a89b2/fastapi_users-15.0.1-py3-none-any.whl", hash = "sha256:6f637eb2fc80be6bba396b77dded30fe4c22fa943349d2e0a1647894f8b21c16", size = 38624 }, ] [package.optional-dependencies] @@ -724,9 +733,9 @@ dependencies = [ { name = "fastapi-users" }, { name = "sqlalchemy", extra = ["asyncio"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/87/12/bc9e6146ae31564741cefc87ee6e37fa5b566933f0afe8aa030779d60e60/fastapi_users_db_sqlalchemy-7.0.0.tar.gz", hash = "sha256:6823eeedf8a92f819276a2b2210ef1dcfd71fe8b6e37f7b4da8d1c60e3dfd595", size = 10877, upload-time = "2025-01-04T13:09:05.086Z" } +sdist = { url = "https://files.pythonhosted.org/packages/87/12/bc9e6146ae31564741cefc87ee6e37fa5b566933f0afe8aa030779d60e60/fastapi_users_db_sqlalchemy-7.0.0.tar.gz", hash = "sha256:6823eeedf8a92f819276a2b2210ef1dcfd71fe8b6e37f7b4da8d1c60e3dfd595", size = 10877 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/08/9968963c1fb8c34627b7f1fbcdfe9438540f87dc7c9bfb59bb4fd19a4ecf/fastapi_users_db_sqlalchemy-7.0.0-py3-none-any.whl", hash = "sha256:5fceac018e7cfa69efc70834dd3035b3de7988eb4274154a0dbe8b14f5aa001e", size = 6891, upload-time = "2025-01-04T13:09:02.869Z" }, + { url = "https://files.pythonhosted.org/packages/a6/08/9968963c1fb8c34627b7f1fbcdfe9438540f87dc7c9bfb59bb4fd19a4ecf/fastapi_users_db_sqlalchemy-7.0.0-py3-none-any.whl", hash = "sha256:5fceac018e7cfa69efc70834dd3035b3de7988eb4274154a0dbe8b14f5aa001e", size = 6891 }, ] [[package]] @@ -743,14 +752,14 @@ dependencies = [ name = "filelock" version = "3.20.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" }, + { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054 }, ] [[package]] name = "google-api-core" -version = "2.26.0" +version = "2.28.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-auth" }, @@ -759,14 +768,14 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/32/ea/e7b6ac3c7b557b728c2d0181010548cbbdd338e9002513420c5a354fa8df/google_api_core-2.26.0.tar.gz", hash = "sha256:e6e6d78bd6cf757f4aee41dcc85b07f485fbb069d5daa3afb126defba1e91a62", size = 166369, upload-time = "2025-10-08T21:37:38.39Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/da/83d7043169ac2c8c7469f0e375610d78ae2160134bf1b80634c482fa079c/google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8", size = 176759 } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/ad/f73cf9fe9bd95918502b270e3ddb8764e4c900b3bbd7782b90c56fac14bb/google_api_core-2.26.0-py3-none-any.whl", hash = "sha256:2b204bd0da2c81f918e3582c48458e24c11771f987f6258e6e227212af78f3ed", size = 162505, upload-time = "2025-10-08T21:37:36.651Z" }, + { url = "https://files.pythonhosted.org/packages/ed/d4/90197b416cb61cefd316964fd9e7bd8324bcbafabf40eef14a9f20b81974/google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c", size = 173706 }, ] [[package]] name = "google-api-python-client" -version = "2.185.0" +version = "2.187.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, @@ -775,85 +784,85 @@ dependencies = [ { name = "httplib2" }, { name = "uritemplate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8e/5a/6f9b49d67ea91376305fdb8bbf2877c746d756e45fd8fb7d2e32d6dad19b/google_api_python_client-2.185.0.tar.gz", hash = "sha256:aa1b338e4bb0f141c2df26743f6b46b11f38705aacd775b61971cbc51da089c3", size = 13885609, upload-time = "2025-10-17T15:00:35.623Z" } +sdist = { url = "https://files.pythonhosted.org/packages/75/83/60cdacf139d768dd7f0fcbe8d95b418299810068093fdf8228c6af89bb70/google_api_python_client-2.187.0.tar.gz", hash = "sha256:e98e8e8f49e1b5048c2f8276473d6485febc76c9c47892a8b4d1afa2c9ec8278", size = 14068154 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/28/be3b17bd6a190c8c2ec9e4fb65d43e6ecd7b7a1bb19ccc1d9ab4f687a58c/google_api_python_client-2.185.0-py3-none-any.whl", hash = "sha256:00fe173a4b346d2397fbe0d37ac15368170dfbed91a0395a66ef2558e22b93fc", size = 14453595, upload-time = "2025-10-17T15:00:33.176Z" }, + { url = "https://files.pythonhosted.org/packages/96/58/c1e716be1b055b504d80db2c8413f6c6a890a6ae218a65f178b63bc30356/google_api_python_client-2.187.0-py3-none-any.whl", hash = "sha256:d8d0f6d85d7d1d10bdab32e642312ed572bdc98919f72f831b44b9a9cebba32f", size = 14641434 }, ] [[package]] name = "google-auth" -version = "2.41.1" +version = "2.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, { name = "pyasn1-modules" }, { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/af/5129ce5b2f9688d2fa49b463e544972a7c82b0fdb50980dafee92e121d9f/google_auth-2.41.1.tar.gz", hash = "sha256:b76b7b1f9e61f0cb7e88870d14f6a94aeef248959ef6992670efee37709cbfd2", size = 292284, upload-time = "2025-09-30T22:51:26.363Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/ef/66d14cf0e01b08d2d51ffc3c20410c4e134a1548fc246a6081eae585a4fe/google_auth-2.43.0.tar.gz", hash = "sha256:88228eee5fc21b62a1b5fe773ca15e67778cb07dc8363adcb4a8827b52d81483", size = 296359 } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/a4/7319a2a8add4cc352be9e3efeff5e2aacee917c85ca2fa1647e29089983c/google_auth-2.41.1-py2.py3-none-any.whl", hash = "sha256:754843be95575b9a19c604a848a41be03f7f2afd8c019f716dc1f51ee41c639d", size = 221302, upload-time = "2025-09-30T22:51:24.212Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d1/385110a9ae86d91cc14c5282c61fe9f4dc41c0b9f7d423c6ad77038c4448/google_auth-2.43.0-py2.py3-none-any.whl", hash = "sha256:af628ba6fa493f75c7e9dbe9373d148ca9f4399b5ea29976519e0a3848eddd16", size = 223114 }, ] [[package]] name = "google-auth-httplib2" -version = "0.2.0" +version = "0.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-auth" }, { name = "httplib2" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/be/217a598a818567b28e859ff087f347475c807a5649296fb5a817c58dacef/google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05", size = 10842, upload-time = "2023-12-12T17:40:30.722Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/83/7ef576d1c7ccea214e7b001e69c006bc75e058a3a1f2ab810167204b698b/google_auth_httplib2-0.2.1.tar.gz", hash = "sha256:5ef03be3927423c87fb69607b42df23a444e434ddb2555b73b3679793187b7de", size = 11086 } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/8a/fe34d2f3f9470a27b01c9e76226965863f153d5fbe276f83608562e49c04/google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d", size = 9253, upload-time = "2023-12-12T17:40:13.055Z" }, + { url = "https://files.pythonhosted.org/packages/44/a7/ca23dd006255f70e2bc469d3f9f0c82ea455335bfd682ad4d677adc435de/google_auth_httplib2-0.2.1-py3-none-any.whl", hash = "sha256:1be94c611db91c01f9703e7f62b0a59bbd5587a95571c7b6fade510d648bc08b", size = 9525 }, ] [[package]] name = "googleapis-common-protos" -version = "1.71.0" +version = "1.72.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/30/43/b25abe02db2911397819003029bef768f68a974f2ece483e6084d1a5f754/googleapis_common_protos-1.71.0.tar.gz", hash = "sha256:1aec01e574e29da63c80ba9f7bbf1ccfaacf1da877f23609fe236ca7c72a2e2e", size = 146454, upload-time = "2025-10-20T14:58:08.732Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433 } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/e8/eba9fece11d57a71e3e22ea672742c8f3cf23b35730c9e96db768b295216/googleapis_common_protos-1.71.0-py3-none-any.whl", hash = "sha256:59034a1d849dc4d18971997a72ac56246570afdd17f9369a0ff68218d50ab78c", size = 294576, upload-time = "2025-10-20T14:56:21.295Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515 }, ] [[package]] name = "greenlet" version = "3.2.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" }, - { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" }, - { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191, upload-time = "2025-08-07T13:45:29.752Z" }, - { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516, upload-time = "2025-08-07T13:53:16.314Z" }, - { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169, upload-time = "2025-08-07T13:18:32.861Z" }, - { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" }, - { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" }, - { url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210, upload-time = "2025-08-07T13:18:24.072Z" }, - { url = "https://files.pythonhosted.org/packages/1c/53/f9c440463b3057485b8594d7a638bed53ba531165ef0ca0e6c364b5cc807/greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b", size = 1564759, upload-time = "2025-11-04T12:42:19.395Z" }, - { url = "https://files.pythonhosted.org/packages/47/e4/3bb4240abdd0a8d23f4f88adec746a3099f0d86bfedb623f063b2e3b4df0/greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929", size = 1634288, upload-time = "2025-11-04T12:42:21.174Z" }, - { url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685, upload-time = "2025-08-07T13:24:38.824Z" }, - { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" }, - { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" }, - { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218, upload-time = "2025-08-07T13:45:30.969Z" }, - { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" }, - { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" }, - { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" }, - { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508, upload-time = "2025-11-04T12:42:23.427Z" }, - { url = "https://files.pythonhosted.org/packages/0d/da/343cd760ab2f92bac1845ca07ee3faea9fe52bee65f7bcb19f16ad7de08b/greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681", size = 1680760, upload-time = "2025-11-04T12:42:25.341Z" }, - { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425, upload-time = "2025-08-07T13:32:27.59Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814 }, + { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073 }, + { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191 }, + { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516 }, + { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169 }, + { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497 }, + { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662 }, + { url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210 }, + { url = "https://files.pythonhosted.org/packages/1c/53/f9c440463b3057485b8594d7a638bed53ba531165ef0ca0e6c364b5cc807/greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b", size = 1564759 }, + { url = "https://files.pythonhosted.org/packages/47/e4/3bb4240abdd0a8d23f4f88adec746a3099f0d86bfedb623f063b2e3b4df0/greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929", size = 1634288 }, + { url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685 }, + { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586 }, + { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346 }, + { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218 }, + { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659 }, + { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355 }, + { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512 }, + { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508 }, + { url = "https://files.pythonhosted.org/packages/0d/da/343cd760ab2f92bac1845ca07ee3faea9fe52bee65f7bcb19f16ad7de08b/greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681", size = 1680760 }, + { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425 }, ] [[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" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } 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" }, + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, ] [[package]] @@ -864,9 +873,9 @@ 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" } +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } 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" }, + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, ] [[package]] @@ -876,31 +885,31 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyparsing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/77/6653db69c1f7ecfe5e3f9726fdadc981794656fcd7d98c4209fecfea9993/httplib2-0.31.0.tar.gz", hash = "sha256:ac7ab497c50975147d4f7b1ade44becc7df2f8954d42b38b3d69c515f531135c", size = 250759, upload-time = "2025-09-11T12:16:03.403Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/77/6653db69c1f7ecfe5e3f9726fdadc981794656fcd7d98c4209fecfea9993/httplib2-0.31.0.tar.gz", hash = "sha256:ac7ab497c50975147d4f7b1ade44becc7df2f8954d42b38b3d69c515f531135c", size = 250759 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/a2/0d269db0f6163be503775dc8b6a6fa15820cc9fdc866f6ba608d86b721f2/httplib2-0.31.0-py3-none-any.whl", hash = "sha256:b9cd78abea9b4e43a7714c6e0f8b6b8561a6fc1e95d5dbd367f5bf0ef35f5d24", size = 91148, upload-time = "2025-09-11T12:16:01.803Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a2/0d269db0f6163be503775dc8b6a6fa15820cc9fdc866f6ba608d86b721f2/httplib2-0.31.0-py3-none-any.whl", hash = "sha256:b9cd78abea9b4e43a7714c6e0f8b6b8561a6fc1e95d5dbd367f5bf0ef35f5d24", size = 91148 }, ] [[package]] name = "httptools" version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961 } wheels = [ - { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" }, - { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" }, - { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" }, - { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" }, - { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" }, - { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" }, - { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" }, - { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" }, - { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" }, - { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" }, - { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" }, - { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" }, - { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" }, - { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" }, + { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889 }, + { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180 }, + { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596 }, + { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268 }, + { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517 }, + { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337 }, + { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743 }, + { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619 }, + { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714 }, + { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909 }, + { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831 }, + { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631 }, + { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910 }, + { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205 }, ] [[package]] @@ -913,9 +922,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } 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" }, + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, ] [[package]] @@ -925,9 +934,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/07/db4ad128da3926be22eec586aa87dafd8840c9eb03fe88505fbed016b5c6/httpx_oauth-0.16.1.tar.gz", hash = "sha256:7402f061f860abc092ea4f5c90acfc576a40bbb79633c1d2920f1ca282c296ee", size = 44148, upload-time = "2024-12-20T07:23:02.589Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/07/db4ad128da3926be22eec586aa87dafd8840c9eb03fe88505fbed016b5c6/httpx_oauth-0.16.1.tar.gz", hash = "sha256:7402f061f860abc092ea4f5c90acfc576a40bbb79633c1d2920f1ca282c296ee", size = 44148 } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/4b/2b81e876abf77b4af3372aff731f4f6722840ebc7dcfd85778eaba271733/httpx_oauth-0.16.1-py3-none-any.whl", hash = "sha256:2fcad82f80f28d0473a0fc4b4eda223dc952050af7e3a8c8781342d850f09fb5", size = 38056, upload-time = "2024-12-20T07:23:00.394Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/2b81e876abf77b4af3372aff731f4f6722840ebc7dcfd85778eaba271733/httpx_oauth-0.16.1-py3-none-any.whl", hash = "sha256:2fcad82f80f28d0473a0fc4b4eda223dc952050af7e3a8c8781342d850f09fb5", size = 38056 }, ] [[package]] @@ -937,36 +946,36 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyreadline3", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" }, + { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794 }, ] [[package]] name = "idna" version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 }, ] [[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" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503 } 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" }, + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484 }, ] [[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" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410 } 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" }, + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234 }, ] [[package]] @@ -976,27 +985,27 @@ 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" } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } 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" }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, ] [[package]] name = "jmespath" version = "1.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843, upload-time = "2022-06-17T18:00:12.224Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" }, + { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256 }, ] [[package]] name = "makefun" version = "1.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/cf/6780ab8bc3b84a1cce3e4400aed3d64b6db7d5e227a2f75b6ded5674701a/makefun-1.16.0.tar.gz", hash = "sha256:e14601831570bff1f6d7e68828bcd30d2f5856f24bad5de0ccb22921ceebc947", size = 73565, upload-time = "2025-05-09T15:00:42.313Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/cf/6780ab8bc3b84a1cce3e4400aed3d64b6db7d5e227a2f75b6ded5674701a/makefun-1.16.0.tar.gz", hash = "sha256:e14601831570bff1f6d7e68828bcd30d2f5856f24bad5de0ccb22921ceebc947", size = 73565 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/c0/4bc973defd1270b89ccaae04cef0d5fa3ea85b59b108ad2c08aeea9afb76/makefun-1.16.0-py2.py3-none-any.whl", hash = "sha256:43baa4c3e7ae2b17de9ceac20b669e9a67ceeadff31581007cca20a07bbe42c4", size = 22923, upload-time = "2025-05-09T15:00:41.042Z" }, + { url = "https://files.pythonhosted.org/packages/b7/c0/4bc973defd1270b89ccaae04cef0d5fa3ea85b59b108ad2c08aeea9afb76/makefun-1.16.0-py2.py3-none-any.whl", hash = "sha256:43baa4c3e7ae2b17de9ceac20b669e9a67ceeadff31581007cca20a07bbe42c4", size = 22923 }, ] [[package]] @@ -1006,18 +1015,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474 } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509 }, ] [[package]] name = "markdown" -version = "3.9" +version = "3.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8d/37/02347f6d6d8279247a5837082ebc26fc0d5aaeaf75aa013fcbb433c777ab/markdown-3.9.tar.gz", hash = "sha256:d2900fe1782bd33bdbbd56859defef70c2e78fc46668f8eb9df3128138f2cb6a", size = 364585, upload-time = "2025-09-04T20:25:22.885Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/7dd27d9d863b3376fcf23a5a13cb5d024aed1db46f963f1b5735ae43b3be/markdown-3.10.tar.gz", hash = "sha256:37062d4f2aa4b2b6b32aefb80faa300f82cc790cb949a35b8caede34f2b68c0e", size = 364931 } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/ae/44c4a6a4cbb496d93c6257954260fe3a6e91b7bed2240e5dad2a717f5111/markdown-3.9-py3-none-any.whl", hash = "sha256:9f4d91ed810864ea88a6f32c07ba8bee1346c0cc1f6b1f9f6c822f2a9667d280", size = 107441, upload-time = "2025-09-04T20:25:21.784Z" }, + { url = "https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl", hash = "sha256:b5b99d6951e2e4948d939255596523444c0e677c669700b1d17aa4a8a464cb7c", size = 107678 }, ] [[package]] @@ -1027,70 +1036,70 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070 } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321 }, ] [[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/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" }, +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } +wheels = [ + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 }, ] [[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" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } 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" }, + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, ] [[package]] @@ -1103,70 +1112,70 @@ dependencies = [ { name = "dotmap" }, { name = "jinja2" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/68/4e0e1b0bc64f0d3afac2fb8a4fb35f2a4e9a0521ae1c777c0e29e21b27fa/mjml-0.11.1.tar.gz", hash = "sha256:f703c8b3458ca0100df6cf56a3633f193b352a80b1a1836a452b92361e74ca73", size = 66589, upload-time = "2025-05-13T10:24:05.693Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/68/4e0e1b0bc64f0d3afac2fb8a4fb35f2a4e9a0521ae1c777c0e29e21b27fa/mjml-0.11.1.tar.gz", hash = "sha256:f703c8b3458ca0100df6cf56a3633f193b352a80b1a1836a452b92361e74ca73", size = 66589 } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/a6/7ed27888adbf8cbdd734e298691004918ec0ef5f40e6bc1329ed97da2273/mjml-0.11.1-py3-none-any.whl", hash = "sha256:fef9f7a95929cbe5ddce9351ee8702e05153d68abc77dcf8e84da2c22a330b2a", size = 63191, upload-time = "2025-05-13T10:24:03.953Z" }, + { url = "https://files.pythonhosted.org/packages/85/a6/7ed27888adbf8cbdd734e298691004918ec0ef5f40e6bc1329ed97da2273/mjml-0.11.1-py3-none-any.whl", hash = "sha256:fef9f7a95929cbe5ddce9351ee8702e05153d68abc77dcf8e84da2c22a330b2a", size = 63191 }, ] [[package]] name = "nodeenv" version = "1.9.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, ] [[package]] name = "numpy" version = "2.3.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/f4/098d2270d52b41f1bd7db9fc288aaa0400cb48c2a3e2af6fa365d9720947/numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a", size = 20582187, upload-time = "2025-10-15T16:18:11.77Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/7e/b72610cc91edf138bc588df5150957a4937221ca6058b825b4725c27be62/numpy-2.3.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c090d4860032b857d94144d1a9976b8e36709e40386db289aaf6672de2a81966", size = 20950335, upload-time = "2025-10-15T16:16:10.304Z" }, - { url = "https://files.pythonhosted.org/packages/3e/46/bdd3370dcea2f95ef14af79dbf81e6927102ddf1cc54adc0024d61252fd9/numpy-2.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a13fc473b6db0be619e45f11f9e81260f7302f8d180c49a22b6e6120022596b3", size = 14179878, upload-time = "2025-10-15T16:16:12.595Z" }, - { url = "https://files.pythonhosted.org/packages/ac/01/5a67cb785bda60f45415d09c2bc245433f1c68dd82eef9c9002c508b5a65/numpy-2.3.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:3634093d0b428e6c32c3a69b78e554f0cd20ee420dcad5a9f3b2a63762ce4197", size = 5108673, upload-time = "2025-10-15T16:16:14.877Z" }, - { url = "https://files.pythonhosted.org/packages/c2/cd/8428e23a9fcebd33988f4cb61208fda832800ca03781f471f3727a820704/numpy-2.3.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:043885b4f7e6e232d7df4f51ffdef8c36320ee9d5f227b380ea636722c7ed12e", size = 6641438, upload-time = "2025-10-15T16:16:16.805Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d1/913fe563820f3c6b079f992458f7331278dcd7ba8427e8e745af37ddb44f/numpy-2.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7", size = 14281290, upload-time = "2025-10-15T16:16:18.764Z" }, - { url = "https://files.pythonhosted.org/packages/9e/7e/7d306ff7cb143e6d975cfa7eb98a93e73495c4deabb7d1b5ecf09ea0fd69/numpy-2.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc8a63918b04b8571789688b2780ab2b4a33ab44bfe8ccea36d3eba51228c953", size = 16636543, upload-time = "2025-10-15T16:16:21.072Z" }, - { url = "https://files.pythonhosted.org/packages/47/6a/8cfc486237e56ccfb0db234945552a557ca266f022d281a2f577b98e955c/numpy-2.3.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40cc556d5abbc54aabe2b1ae287042d7bdb80c08edede19f0c0afb36ae586f37", size = 16056117, upload-time = "2025-10-15T16:16:23.369Z" }, - { url = "https://files.pythonhosted.org/packages/b1/0e/42cb5e69ea901e06ce24bfcc4b5664a56f950a70efdcf221f30d9615f3f3/numpy-2.3.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ecb63014bb7f4ce653f8be7f1df8cbc6093a5a2811211770f6606cc92b5a78fd", size = 18577788, upload-time = "2025-10-15T16:16:27.496Z" }, - { url = "https://files.pythonhosted.org/packages/86/92/41c3d5157d3177559ef0a35da50f0cda7fa071f4ba2306dd36818591a5bc/numpy-2.3.4-cp313-cp313-win32.whl", hash = "sha256:e8370eb6925bb8c1c4264fec52b0384b44f675f191df91cbe0140ec9f0955646", size = 6282620, upload-time = "2025-10-15T16:16:29.811Z" }, - { url = "https://files.pythonhosted.org/packages/09/97/fd421e8bc50766665ad35536c2bb4ef916533ba1fdd053a62d96cc7c8b95/numpy-2.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:56209416e81a7893036eea03abcb91c130643eb14233b2515c90dcac963fe99d", size = 12784672, upload-time = "2025-10-15T16:16:31.589Z" }, - { url = "https://files.pythonhosted.org/packages/ad/df/5474fb2f74970ca8eb978093969b125a84cc3d30e47f82191f981f13a8a0/numpy-2.3.4-cp313-cp313-win_arm64.whl", hash = "sha256:a700a4031bc0fd6936e78a752eefb79092cecad2599ea9c8039c548bc097f9bc", size = 10196702, upload-time = "2025-10-15T16:16:33.902Z" }, - { url = "https://files.pythonhosted.org/packages/11/83/66ac031464ec1767ea3ed48ce40f615eb441072945e98693bec0bcd056cc/numpy-2.3.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:86966db35c4040fdca64f0816a1c1dd8dbd027d90fca5a57e00e1ca4cd41b879", size = 21049003, upload-time = "2025-10-15T16:16:36.101Z" }, - { url = "https://files.pythonhosted.org/packages/5f/99/5b14e0e686e61371659a1d5bebd04596b1d72227ce36eed121bb0aeab798/numpy-2.3.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:838f045478638b26c375ee96ea89464d38428c69170360b23a1a50fa4baa3562", size = 14302980, upload-time = "2025-10-15T16:16:39.124Z" }, - { url = "https://files.pythonhosted.org/packages/2c/44/e9486649cd087d9fc6920e3fc3ac2aba10838d10804b1e179fb7cbc4e634/numpy-2.3.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d7315ed1dab0286adca467377c8381cd748f3dc92235f22a7dfc42745644a96a", size = 5231472, upload-time = "2025-10-15T16:16:41.168Z" }, - { url = "https://files.pythonhosted.org/packages/3e/51/902b24fa8887e5fe2063fd61b1895a476d0bbf46811ab0c7fdf4bd127345/numpy-2.3.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:84f01a4d18b2cc4ade1814a08e5f3c907b079c847051d720fad15ce37aa930b6", size = 6739342, upload-time = "2025-10-15T16:16:43.777Z" }, - { url = "https://files.pythonhosted.org/packages/34/f1/4de9586d05b1962acdcdb1dc4af6646361a643f8c864cef7c852bf509740/numpy-2.3.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:817e719a868f0dacde4abdfc5c1910b301877970195db9ab6a5e2c4bd5b121f7", size = 14354338, upload-time = "2025-10-15T16:16:46.081Z" }, - { url = "https://files.pythonhosted.org/packages/1f/06/1c16103b425de7969d5a76bdf5ada0804b476fed05d5f9e17b777f1cbefd/numpy-2.3.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85e071da78d92a214212cacea81c6da557cab307f2c34b5f85b628e94803f9c0", size = 16702392, upload-time = "2025-10-15T16:16:48.455Z" }, - { url = "https://files.pythonhosted.org/packages/34/b2/65f4dc1b89b5322093572b6e55161bb42e3e0487067af73627f795cc9d47/numpy-2.3.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2ec646892819370cf3558f518797f16597b4e4669894a2ba712caccc9da53f1f", size = 16134998, upload-time = "2025-10-15T16:16:51.114Z" }, - { url = "https://files.pythonhosted.org/packages/d4/11/94ec578896cdb973aaf56425d6c7f2aff4186a5c00fac15ff2ec46998b46/numpy-2.3.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:035796aaaddfe2f9664b9a9372f089cfc88bd795a67bd1bfe15e6e770934cf64", size = 18651574, upload-time = "2025-10-15T16:16:53.429Z" }, - { url = "https://files.pythonhosted.org/packages/62/b7/7efa763ab33dbccf56dade36938a77345ce8e8192d6b39e470ca25ff3cd0/numpy-2.3.4-cp313-cp313t-win32.whl", hash = "sha256:fea80f4f4cf83b54c3a051f2f727870ee51e22f0248d3114b8e755d160b38cfb", size = 6413135, upload-time = "2025-10-15T16:16:55.992Z" }, - { url = "https://files.pythonhosted.org/packages/43/70/aba4c38e8400abcc2f345e13d972fb36c26409b3e644366db7649015f291/numpy-2.3.4-cp313-cp313t-win_amd64.whl", hash = "sha256:15eea9f306b98e0be91eb344a94c0e630689ef302e10c2ce5f7e11905c704f9c", size = 12928582, upload-time = "2025-10-15T16:16:57.943Z" }, - { url = "https://files.pythonhosted.org/packages/67/63/871fad5f0073fc00fbbdd7232962ea1ac40eeaae2bba66c76214f7954236/numpy-2.3.4-cp313-cp313t-win_arm64.whl", hash = "sha256:b6c231c9c2fadbae4011ca5e7e83e12dc4a5072f1a1d85a0a7b3ed754d145a40", size = 10266691, upload-time = "2025-10-15T16:17:00.048Z" }, - { url = "https://files.pythonhosted.org/packages/72/71/ae6170143c115732470ae3a2d01512870dd16e0953f8a6dc89525696069b/numpy-2.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81c3e6d8c97295a7360d367f9f8553973651b76907988bb6066376bc2252f24e", size = 20955580, upload-time = "2025-10-15T16:17:02.509Z" }, - { url = "https://files.pythonhosted.org/packages/af/39/4be9222ffd6ca8a30eda033d5f753276a9c3426c397bb137d8e19dedd200/numpy-2.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7c26b0b2bf58009ed1f38a641f3db4be8d960a417ca96d14e5b06df1506d41ff", size = 14188056, upload-time = "2025-10-15T16:17:04.873Z" }, - { url = "https://files.pythonhosted.org/packages/6c/3d/d85f6700d0a4aa4f9491030e1021c2b2b7421b2b38d01acd16734a2bfdc7/numpy-2.3.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:62b2198c438058a20b6704351b35a1d7db881812d8512d67a69c9de1f18ca05f", size = 5116555, upload-time = "2025-10-15T16:17:07.499Z" }, - { url = "https://files.pythonhosted.org/packages/bf/04/82c1467d86f47eee8a19a464c92f90a9bb68ccf14a54c5224d7031241ffb/numpy-2.3.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:9d729d60f8d53a7361707f4b68a9663c968882dd4f09e0d58c044c8bf5faee7b", size = 6643581, upload-time = "2025-10-15T16:17:09.774Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d3/c79841741b837e293f48bd7db89d0ac7a4f2503b382b78a790ef1dc778a5/numpy-2.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd0c630cf256b0a7fd9d0a11c9413b42fef5101219ce6ed5a09624f5a65392c7", size = 14299186, upload-time = "2025-10-15T16:17:11.937Z" }, - { url = "https://files.pythonhosted.org/packages/e8/7e/4a14a769741fbf237eec5a12a2cbc7a4c4e061852b6533bcb9e9a796c908/numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5e081bc082825f8b139f9e9fe42942cb4054524598aaeb177ff476cc76d09d2", size = 16638601, upload-time = "2025-10-15T16:17:14.391Z" }, - { url = "https://files.pythonhosted.org/packages/93/87/1c1de269f002ff0a41173fe01dcc925f4ecff59264cd8f96cf3b60d12c9b/numpy-2.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15fb27364ed84114438fff8aaf998c9e19adbeba08c0b75409f8c452a8692c52", size = 16074219, upload-time = "2025-10-15T16:17:17.058Z" }, - { url = "https://files.pythonhosted.org/packages/cd/28/18f72ee77408e40a76d691001ae599e712ca2a47ddd2c4f695b16c65f077/numpy-2.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:85d9fb2d8cd998c84d13a79a09cc0c1091648e848e4e6249b0ccd7f6b487fa26", size = 18576702, upload-time = "2025-10-15T16:17:19.379Z" }, - { url = "https://files.pythonhosted.org/packages/c3/76/95650169b465ececa8cf4b2e8f6df255d4bf662775e797ade2025cc51ae6/numpy-2.3.4-cp314-cp314-win32.whl", hash = "sha256:e73d63fd04e3a9d6bc187f5455d81abfad05660b212c8804bf3b407e984cd2bc", size = 6337136, upload-time = "2025-10-15T16:17:22.886Z" }, - { url = "https://files.pythonhosted.org/packages/dc/89/a231a5c43ede5d6f77ba4a91e915a87dea4aeea76560ba4d2bf185c683f0/numpy-2.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:3da3491cee49cf16157e70f607c03a217ea6647b1cea4819c4f48e53d49139b9", size = 12920542, upload-time = "2025-10-15T16:17:24.783Z" }, - { url = "https://files.pythonhosted.org/packages/0d/0c/ae9434a888f717c5ed2ff2393b3f344f0ff6f1c793519fa0c540461dc530/numpy-2.3.4-cp314-cp314-win_arm64.whl", hash = "sha256:6d9cd732068e8288dbe2717177320723ccec4fb064123f0caf9bbd90ab5be868", size = 10480213, upload-time = "2025-10-15T16:17:26.935Z" }, - { url = "https://files.pythonhosted.org/packages/83/4b/c4a5f0841f92536f6b9592694a5b5f68c9ab37b775ff342649eadf9055d3/numpy-2.3.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:22758999b256b595cf0b1d102b133bb61866ba5ceecf15f759623b64c020c9ec", size = 21052280, upload-time = "2025-10-15T16:17:29.638Z" }, - { url = "https://files.pythonhosted.org/packages/3e/80/90308845fc93b984d2cc96d83e2324ce8ad1fd6efea81b324cba4b673854/numpy-2.3.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9cb177bc55b010b19798dc5497d540dea67fd13a8d9e882b2dae71de0cf09eb3", size = 14302930, upload-time = "2025-10-15T16:17:32.384Z" }, - { url = "https://files.pythonhosted.org/packages/3d/4e/07439f22f2a3b247cec4d63a713faae55e1141a36e77fb212881f7cda3fb/numpy-2.3.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0f2bcc76f1e05e5ab58893407c63d90b2029908fa41f9f1cc51eecce936c3365", size = 5231504, upload-time = "2025-10-15T16:17:34.515Z" }, - { url = "https://files.pythonhosted.org/packages/ab/de/1e11f2547e2fe3d00482b19721855348b94ada8359aef5d40dd57bfae9df/numpy-2.3.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dc20bde86802df2ed8397a08d793da0ad7a5fd4ea3ac85d757bf5dd4ad7c252", size = 6739405, upload-time = "2025-10-15T16:17:36.128Z" }, - { url = "https://files.pythonhosted.org/packages/3b/40/8cd57393a26cebe2e923005db5134a946c62fa56a1087dc7c478f3e30837/numpy-2.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e199c087e2aa71c8f9ce1cb7a8e10677dc12457e7cc1be4798632da37c3e86e", size = 14354866, upload-time = "2025-10-15T16:17:38.884Z" }, - { url = "https://files.pythonhosted.org/packages/93/39/5b3510f023f96874ee6fea2e40dfa99313a00bf3ab779f3c92978f34aace/numpy-2.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85597b2d25ddf655495e2363fe044b0ae999b75bc4d630dc0d886484b03a5eb0", size = 16703296, upload-time = "2025-10-15T16:17:41.564Z" }, - { url = "https://files.pythonhosted.org/packages/41/0d/19bb163617c8045209c1996c4e427bccbc4bbff1e2c711f39203c8ddbb4a/numpy-2.3.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04a69abe45b49c5955923cf2c407843d1c85013b424ae8a560bba16c92fe44a0", size = 16136046, upload-time = "2025-10-15T16:17:43.901Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c1/6dba12fdf68b02a21ac411c9df19afa66bed2540f467150ca64d246b463d/numpy-2.3.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e1708fac43ef8b419c975926ce1eaf793b0c13b7356cfab6ab0dc34c0a02ac0f", size = 18652691, upload-time = "2025-10-15T16:17:46.247Z" }, - { url = "https://files.pythonhosted.org/packages/f8/73/f85056701dbbbb910c51d846c58d29fd46b30eecd2b6ba760fc8b8a1641b/numpy-2.3.4-cp314-cp314t-win32.whl", hash = "sha256:863e3b5f4d9915aaf1b8ec79ae560ad21f0b8d5e3adc31e73126491bb86dee1d", size = 6485782, upload-time = "2025-10-15T16:17:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/17/90/28fa6f9865181cb817c2471ee65678afa8a7e2a1fb16141473d5fa6bacc3/numpy-2.3.4-cp314-cp314t-win_amd64.whl", hash = "sha256:962064de37b9aef801d33bc579690f8bfe6c5e70e29b61783f60bcba838a14d6", size = 13113301, upload-time = "2025-10-15T16:17:50.938Z" }, - { url = "https://files.pythonhosted.org/packages/54/23/08c002201a8e7e1f9afba93b97deceb813252d9cfd0d3351caed123dcf97/numpy-2.3.4-cp314-cp314t-win_arm64.whl", hash = "sha256:8b5a9a39c45d852b62693d9b3f3e0fe052541f804296ff401a72a1b60edafb29", size = 10547532, upload-time = "2025-10-15T16:17:53.48Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/b5/f4/098d2270d52b41f1bd7db9fc288aaa0400cb48c2a3e2af6fa365d9720947/numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a", size = 20582187 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/7e/b72610cc91edf138bc588df5150957a4937221ca6058b825b4725c27be62/numpy-2.3.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c090d4860032b857d94144d1a9976b8e36709e40386db289aaf6672de2a81966", size = 20950335 }, + { url = "https://files.pythonhosted.org/packages/3e/46/bdd3370dcea2f95ef14af79dbf81e6927102ddf1cc54adc0024d61252fd9/numpy-2.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a13fc473b6db0be619e45f11f9e81260f7302f8d180c49a22b6e6120022596b3", size = 14179878 }, + { url = "https://files.pythonhosted.org/packages/ac/01/5a67cb785bda60f45415d09c2bc245433f1c68dd82eef9c9002c508b5a65/numpy-2.3.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:3634093d0b428e6c32c3a69b78e554f0cd20ee420dcad5a9f3b2a63762ce4197", size = 5108673 }, + { url = "https://files.pythonhosted.org/packages/c2/cd/8428e23a9fcebd33988f4cb61208fda832800ca03781f471f3727a820704/numpy-2.3.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:043885b4f7e6e232d7df4f51ffdef8c36320ee9d5f227b380ea636722c7ed12e", size = 6641438 }, + { url = "https://files.pythonhosted.org/packages/3e/d1/913fe563820f3c6b079f992458f7331278dcd7ba8427e8e745af37ddb44f/numpy-2.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7", size = 14281290 }, + { url = "https://files.pythonhosted.org/packages/9e/7e/7d306ff7cb143e6d975cfa7eb98a93e73495c4deabb7d1b5ecf09ea0fd69/numpy-2.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc8a63918b04b8571789688b2780ab2b4a33ab44bfe8ccea36d3eba51228c953", size = 16636543 }, + { url = "https://files.pythonhosted.org/packages/47/6a/8cfc486237e56ccfb0db234945552a557ca266f022d281a2f577b98e955c/numpy-2.3.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40cc556d5abbc54aabe2b1ae287042d7bdb80c08edede19f0c0afb36ae586f37", size = 16056117 }, + { url = "https://files.pythonhosted.org/packages/b1/0e/42cb5e69ea901e06ce24bfcc4b5664a56f950a70efdcf221f30d9615f3f3/numpy-2.3.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ecb63014bb7f4ce653f8be7f1df8cbc6093a5a2811211770f6606cc92b5a78fd", size = 18577788 }, + { url = "https://files.pythonhosted.org/packages/86/92/41c3d5157d3177559ef0a35da50f0cda7fa071f4ba2306dd36818591a5bc/numpy-2.3.4-cp313-cp313-win32.whl", hash = "sha256:e8370eb6925bb8c1c4264fec52b0384b44f675f191df91cbe0140ec9f0955646", size = 6282620 }, + { url = "https://files.pythonhosted.org/packages/09/97/fd421e8bc50766665ad35536c2bb4ef916533ba1fdd053a62d96cc7c8b95/numpy-2.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:56209416e81a7893036eea03abcb91c130643eb14233b2515c90dcac963fe99d", size = 12784672 }, + { url = "https://files.pythonhosted.org/packages/ad/df/5474fb2f74970ca8eb978093969b125a84cc3d30e47f82191f981f13a8a0/numpy-2.3.4-cp313-cp313-win_arm64.whl", hash = "sha256:a700a4031bc0fd6936e78a752eefb79092cecad2599ea9c8039c548bc097f9bc", size = 10196702 }, + { url = "https://files.pythonhosted.org/packages/11/83/66ac031464ec1767ea3ed48ce40f615eb441072945e98693bec0bcd056cc/numpy-2.3.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:86966db35c4040fdca64f0816a1c1dd8dbd027d90fca5a57e00e1ca4cd41b879", size = 21049003 }, + { url = "https://files.pythonhosted.org/packages/5f/99/5b14e0e686e61371659a1d5bebd04596b1d72227ce36eed121bb0aeab798/numpy-2.3.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:838f045478638b26c375ee96ea89464d38428c69170360b23a1a50fa4baa3562", size = 14302980 }, + { url = "https://files.pythonhosted.org/packages/2c/44/e9486649cd087d9fc6920e3fc3ac2aba10838d10804b1e179fb7cbc4e634/numpy-2.3.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d7315ed1dab0286adca467377c8381cd748f3dc92235f22a7dfc42745644a96a", size = 5231472 }, + { url = "https://files.pythonhosted.org/packages/3e/51/902b24fa8887e5fe2063fd61b1895a476d0bbf46811ab0c7fdf4bd127345/numpy-2.3.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:84f01a4d18b2cc4ade1814a08e5f3c907b079c847051d720fad15ce37aa930b6", size = 6739342 }, + { url = "https://files.pythonhosted.org/packages/34/f1/4de9586d05b1962acdcdb1dc4af6646361a643f8c864cef7c852bf509740/numpy-2.3.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:817e719a868f0dacde4abdfc5c1910b301877970195db9ab6a5e2c4bd5b121f7", size = 14354338 }, + { url = "https://files.pythonhosted.org/packages/1f/06/1c16103b425de7969d5a76bdf5ada0804b476fed05d5f9e17b777f1cbefd/numpy-2.3.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85e071da78d92a214212cacea81c6da557cab307f2c34b5f85b628e94803f9c0", size = 16702392 }, + { url = "https://files.pythonhosted.org/packages/34/b2/65f4dc1b89b5322093572b6e55161bb42e3e0487067af73627f795cc9d47/numpy-2.3.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2ec646892819370cf3558f518797f16597b4e4669894a2ba712caccc9da53f1f", size = 16134998 }, + { url = "https://files.pythonhosted.org/packages/d4/11/94ec578896cdb973aaf56425d6c7f2aff4186a5c00fac15ff2ec46998b46/numpy-2.3.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:035796aaaddfe2f9664b9a9372f089cfc88bd795a67bd1bfe15e6e770934cf64", size = 18651574 }, + { url = "https://files.pythonhosted.org/packages/62/b7/7efa763ab33dbccf56dade36938a77345ce8e8192d6b39e470ca25ff3cd0/numpy-2.3.4-cp313-cp313t-win32.whl", hash = "sha256:fea80f4f4cf83b54c3a051f2f727870ee51e22f0248d3114b8e755d160b38cfb", size = 6413135 }, + { url = "https://files.pythonhosted.org/packages/43/70/aba4c38e8400abcc2f345e13d972fb36c26409b3e644366db7649015f291/numpy-2.3.4-cp313-cp313t-win_amd64.whl", hash = "sha256:15eea9f306b98e0be91eb344a94c0e630689ef302e10c2ce5f7e11905c704f9c", size = 12928582 }, + { url = "https://files.pythonhosted.org/packages/67/63/871fad5f0073fc00fbbdd7232962ea1ac40eeaae2bba66c76214f7954236/numpy-2.3.4-cp313-cp313t-win_arm64.whl", hash = "sha256:b6c231c9c2fadbae4011ca5e7e83e12dc4a5072f1a1d85a0a7b3ed754d145a40", size = 10266691 }, + { url = "https://files.pythonhosted.org/packages/72/71/ae6170143c115732470ae3a2d01512870dd16e0953f8a6dc89525696069b/numpy-2.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81c3e6d8c97295a7360d367f9f8553973651b76907988bb6066376bc2252f24e", size = 20955580 }, + { url = "https://files.pythonhosted.org/packages/af/39/4be9222ffd6ca8a30eda033d5f753276a9c3426c397bb137d8e19dedd200/numpy-2.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7c26b0b2bf58009ed1f38a641f3db4be8d960a417ca96d14e5b06df1506d41ff", size = 14188056 }, + { url = "https://files.pythonhosted.org/packages/6c/3d/d85f6700d0a4aa4f9491030e1021c2b2b7421b2b38d01acd16734a2bfdc7/numpy-2.3.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:62b2198c438058a20b6704351b35a1d7db881812d8512d67a69c9de1f18ca05f", size = 5116555 }, + { url = "https://files.pythonhosted.org/packages/bf/04/82c1467d86f47eee8a19a464c92f90a9bb68ccf14a54c5224d7031241ffb/numpy-2.3.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:9d729d60f8d53a7361707f4b68a9663c968882dd4f09e0d58c044c8bf5faee7b", size = 6643581 }, + { url = "https://files.pythonhosted.org/packages/0c/d3/c79841741b837e293f48bd7db89d0ac7a4f2503b382b78a790ef1dc778a5/numpy-2.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd0c630cf256b0a7fd9d0a11c9413b42fef5101219ce6ed5a09624f5a65392c7", size = 14299186 }, + { url = "https://files.pythonhosted.org/packages/e8/7e/4a14a769741fbf237eec5a12a2cbc7a4c4e061852b6533bcb9e9a796c908/numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5e081bc082825f8b139f9e9fe42942cb4054524598aaeb177ff476cc76d09d2", size = 16638601 }, + { url = "https://files.pythonhosted.org/packages/93/87/1c1de269f002ff0a41173fe01dcc925f4ecff59264cd8f96cf3b60d12c9b/numpy-2.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15fb27364ed84114438fff8aaf998c9e19adbeba08c0b75409f8c452a8692c52", size = 16074219 }, + { url = "https://files.pythonhosted.org/packages/cd/28/18f72ee77408e40a76d691001ae599e712ca2a47ddd2c4f695b16c65f077/numpy-2.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:85d9fb2d8cd998c84d13a79a09cc0c1091648e848e4e6249b0ccd7f6b487fa26", size = 18576702 }, + { url = "https://files.pythonhosted.org/packages/c3/76/95650169b465ececa8cf4b2e8f6df255d4bf662775e797ade2025cc51ae6/numpy-2.3.4-cp314-cp314-win32.whl", hash = "sha256:e73d63fd04e3a9d6bc187f5455d81abfad05660b212c8804bf3b407e984cd2bc", size = 6337136 }, + { url = "https://files.pythonhosted.org/packages/dc/89/a231a5c43ede5d6f77ba4a91e915a87dea4aeea76560ba4d2bf185c683f0/numpy-2.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:3da3491cee49cf16157e70f607c03a217ea6647b1cea4819c4f48e53d49139b9", size = 12920542 }, + { url = "https://files.pythonhosted.org/packages/0d/0c/ae9434a888f717c5ed2ff2393b3f344f0ff6f1c793519fa0c540461dc530/numpy-2.3.4-cp314-cp314-win_arm64.whl", hash = "sha256:6d9cd732068e8288dbe2717177320723ccec4fb064123f0caf9bbd90ab5be868", size = 10480213 }, + { url = "https://files.pythonhosted.org/packages/83/4b/c4a5f0841f92536f6b9592694a5b5f68c9ab37b775ff342649eadf9055d3/numpy-2.3.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:22758999b256b595cf0b1d102b133bb61866ba5ceecf15f759623b64c020c9ec", size = 21052280 }, + { url = "https://files.pythonhosted.org/packages/3e/80/90308845fc93b984d2cc96d83e2324ce8ad1fd6efea81b324cba4b673854/numpy-2.3.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9cb177bc55b010b19798dc5497d540dea67fd13a8d9e882b2dae71de0cf09eb3", size = 14302930 }, + { url = "https://files.pythonhosted.org/packages/3d/4e/07439f22f2a3b247cec4d63a713faae55e1141a36e77fb212881f7cda3fb/numpy-2.3.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0f2bcc76f1e05e5ab58893407c63d90b2029908fa41f9f1cc51eecce936c3365", size = 5231504 }, + { url = "https://files.pythonhosted.org/packages/ab/de/1e11f2547e2fe3d00482b19721855348b94ada8359aef5d40dd57bfae9df/numpy-2.3.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dc20bde86802df2ed8397a08d793da0ad7a5fd4ea3ac85d757bf5dd4ad7c252", size = 6739405 }, + { url = "https://files.pythonhosted.org/packages/3b/40/8cd57393a26cebe2e923005db5134a946c62fa56a1087dc7c478f3e30837/numpy-2.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e199c087e2aa71c8f9ce1cb7a8e10677dc12457e7cc1be4798632da37c3e86e", size = 14354866 }, + { url = "https://files.pythonhosted.org/packages/93/39/5b3510f023f96874ee6fea2e40dfa99313a00bf3ab779f3c92978f34aace/numpy-2.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85597b2d25ddf655495e2363fe044b0ae999b75bc4d630dc0d886484b03a5eb0", size = 16703296 }, + { url = "https://files.pythonhosted.org/packages/41/0d/19bb163617c8045209c1996c4e427bccbc4bbff1e2c711f39203c8ddbb4a/numpy-2.3.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04a69abe45b49c5955923cf2c407843d1c85013b424ae8a560bba16c92fe44a0", size = 16136046 }, + { url = "https://files.pythonhosted.org/packages/e2/c1/6dba12fdf68b02a21ac411c9df19afa66bed2540f467150ca64d246b463d/numpy-2.3.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e1708fac43ef8b419c975926ce1eaf793b0c13b7356cfab6ab0dc34c0a02ac0f", size = 18652691 }, + { url = "https://files.pythonhosted.org/packages/f8/73/f85056701dbbbb910c51d846c58d29fd46b30eecd2b6ba760fc8b8a1641b/numpy-2.3.4-cp314-cp314t-win32.whl", hash = "sha256:863e3b5f4d9915aaf1b8ec79ae560ad21f0b8d5e3adc31e73126491bb86dee1d", size = 6485782 }, + { url = "https://files.pythonhosted.org/packages/17/90/28fa6f9865181cb817c2471ee65678afa8a7e2a1fb16141473d5fa6bacc3/numpy-2.3.4-cp314-cp314t-win_amd64.whl", hash = "sha256:962064de37b9aef801d33bc579690f8bfe6c5e70e29b61783f60bcba838a14d6", size = 13113301 }, + { url = "https://files.pythonhosted.org/packages/54/23/08c002201a8e7e1f9afba93b97deceb813252d9cfd0d3351caed123dcf97/numpy-2.3.4-cp314-cp314t-win_arm64.whl", hash = "sha256:8b5a9a39c45d852b62693d9b3f3e0fe052541f804296ff401a72a1b60edafb29", size = 10547532 }, ] [[package]] @@ -1176,18 +1185,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "et-xmlfile" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" }, + { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910 }, ] [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, ] [[package]] @@ -1200,34 +1209,34 @@ dependencies = [ { name = "pytz" }, { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, - { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, - { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, - { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, - { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, - { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, - { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, - { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, - { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, - { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, - { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, - { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, - { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, - { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, - { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, - { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, - { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, - { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, - { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, - { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, - { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, - { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, - { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, - { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, - { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671 }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807 }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872 }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371 }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333 }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120 }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991 }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227 }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056 }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189 }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912 }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160 }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233 }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635 }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079 }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049 }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638 }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834 }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925 }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071 }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504 }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702 }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535 }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582 }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963 }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175 }, ] [[package]] @@ -1239,76 +1248,76 @@ dependencies = [ { name = "sqlalchemy" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/fe/84f4d06ac3e6038384847dc1d5c8b956f61b780f69509d177107b550c7b9/paracelsus-0.12.0.tar.gz", hash = "sha256:f1d8f584ebc445db99a2906f97ff55f36ae663c104320dd4a6b5b78b4fa24dce", size = 83664, upload-time = "2025-10-07T12:45:41.112Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fe/84f4d06ac3e6038384847dc1d5c8b956f61b780f69509d177107b550c7b9/paracelsus-0.12.0.tar.gz", hash = "sha256:f1d8f584ebc445db99a2906f97ff55f36ae663c104320dd4a6b5b78b4fa24dce", size = 83664 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/60/9062e4072c16750b6b01bac9c55b329b249ee7c970d61c128049be197d7a/paracelsus-0.12.0-py3-none-any.whl", hash = "sha256:01f5a508174d06a86d53374215a0c85962498361ac3f0bd3450023760d3b3836", size = 81236, upload-time = "2025-10-07T12:45:39.929Z" }, + { url = "https://files.pythonhosted.org/packages/b3/60/9062e4072c16750b6b01bac9c55b329b249ee7c970d61c128049be197d7a/paracelsus-0.12.0-py3-none-any.whl", hash = "sha256:01f5a508174d06a86d53374215a0c85962498361ac3f0bd3450023760d3b3836", size = 81236 }, ] [[package]] name = "pillow" version = "12.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828, upload-time = "2025-10-15T18:24:14.008Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/f2/de993bb2d21b33a98d031ecf6a978e4b61da207bef02f7b43093774c480d/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", size = 4045493, upload-time = "2025-10-15T18:22:25.758Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b6/bc8d0c4c9f6f111a783d045310945deb769b806d7574764234ffd50bc5ea/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", size = 4120461, upload-time = "2025-10-15T18:22:27.286Z" }, - { url = "https://files.pythonhosted.org/packages/5d/57/d60d343709366a353dc56adb4ee1e7d8a2cc34e3fbc22905f4167cfec119/pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", size = 3576912, upload-time = "2025-10-15T18:22:28.751Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a4/a0a31467e3f83b94d37568294b01d22b43ae3c5d85f2811769b9c66389dd/pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", size = 5249132, upload-time = "2025-10-15T18:22:30.641Z" }, - { url = "https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", size = 4650099, upload-time = "2025-10-15T18:22:32.73Z" }, - { url = "https://files.pythonhosted.org/packages/fc/bd/69ed99fd46a8dba7c1887156d3572fe4484e3f031405fcc5a92e31c04035/pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", size = 6230808, upload-time = "2025-10-15T18:22:34.337Z" }, - { url = "https://files.pythonhosted.org/packages/ea/94/8fad659bcdbf86ed70099cb60ae40be6acca434bbc8c4c0d4ef356d7e0de/pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", size = 8037804, upload-time = "2025-10-15T18:22:36.402Z" }, - { url = "https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", size = 6345553, upload-time = "2025-10-15T18:22:38.066Z" }, - { url = "https://files.pythonhosted.org/packages/38/57/755dbd06530a27a5ed74f8cb0a7a44a21722ebf318edbe67ddbd7fb28f88/pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", size = 7037729, upload-time = "2025-10-15T18:22:39.769Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b6/7e94f4c41d238615674d06ed677c14883103dce1c52e4af16f000338cfd7/pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", size = 6459789, upload-time = "2025-10-15T18:22:41.437Z" }, - { url = "https://files.pythonhosted.org/packages/9c/14/4448bb0b5e0f22dd865290536d20ec8a23b64e2d04280b89139f09a36bb6/pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", size = 7130917, upload-time = "2025-10-15T18:22:43.152Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ca/16c6926cc1c015845745d5c16c9358e24282f1e588237a4c36d2b30f182f/pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", size = 6302391, upload-time = "2025-10-15T18:22:44.753Z" }, - { url = "https://files.pythonhosted.org/packages/6d/2a/dd43dcfd6dae9b6a49ee28a8eedb98c7d5ff2de94a5d834565164667b97b/pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", size = 7007477, upload-time = "2025-10-15T18:22:46.838Z" }, - { url = "https://files.pythonhosted.org/packages/77/f0/72ea067f4b5ae5ead653053212af05ce3705807906ba3f3e8f58ddf617e6/pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", size = 2435918, upload-time = "2025-10-15T18:22:48.399Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5e/9046b423735c21f0487ea6cb5b10f89ea8f8dfbe32576fe052b5ba9d4e5b/pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", size = 5251406, upload-time = "2025-10-15T18:22:49.905Z" }, - { url = "https://files.pythonhosted.org/packages/12/66/982ceebcdb13c97270ef7a56c3969635b4ee7cd45227fa707c94719229c5/pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", size = 4653218, upload-time = "2025-10-15T18:22:51.587Z" }, - { url = "https://files.pythonhosted.org/packages/16/b3/81e625524688c31859450119bf12674619429cab3119eec0e30a7a1029cb/pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", size = 6266564, upload-time = "2025-10-15T18:22:53.215Z" }, - { url = "https://files.pythonhosted.org/packages/98/59/dfb38f2a41240d2408096e1a76c671d0a105a4a8471b1871c6902719450c/pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", size = 8069260, upload-time = "2025-10-15T18:22:54.933Z" }, - { url = "https://files.pythonhosted.org/packages/dc/3d/378dbea5cd1874b94c312425ca77b0f47776c78e0df2df751b820c8c1d6c/pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", size = 6379248, upload-time = "2025-10-15T18:22:56.605Z" }, - { url = "https://files.pythonhosted.org/packages/84/b0/d525ef47d71590f1621510327acec75ae58c721dc071b17d8d652ca494d8/pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", size = 7066043, upload-time = "2025-10-15T18:22:58.53Z" }, - { url = "https://files.pythonhosted.org/packages/61/2c/aced60e9cf9d0cde341d54bf7932c9ffc33ddb4a1595798b3a5150c7ec4e/pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", size = 6490915, upload-time = "2025-10-15T18:23:00.582Z" }, - { url = "https://files.pythonhosted.org/packages/ef/26/69dcb9b91f4e59f8f34b2332a4a0a951b44f547c4ed39d3e4dcfcff48f89/pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", size = 7157998, upload-time = "2025-10-15T18:23:02.627Z" }, - { url = "https://files.pythonhosted.org/packages/61/2b/726235842220ca95fa441ddf55dd2382b52ab5b8d9c0596fe6b3f23dafe8/pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", size = 6306201, upload-time = "2025-10-15T18:23:04.709Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3d/2afaf4e840b2df71344ababf2f8edd75a705ce500e5dc1e7227808312ae1/pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", size = 7013165, upload-time = "2025-10-15T18:23:06.46Z" }, - { url = "https://files.pythonhosted.org/packages/6f/75/3fa09aa5cf6ed04bee3fa575798ddf1ce0bace8edb47249c798077a81f7f/pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", size = 2437834, upload-time = "2025-10-15T18:23:08.194Z" }, - { url = "https://files.pythonhosted.org/packages/54/2a/9a8c6ba2c2c07b71bec92cf63e03370ca5e5f5c5b119b742bcc0cde3f9c5/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", size = 4045531, upload-time = "2025-10-15T18:23:10.121Z" }, - { url = "https://files.pythonhosted.org/packages/84/54/836fdbf1bfb3d66a59f0189ff0b9f5f666cee09c6188309300df04ad71fa/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", size = 4120554, upload-time = "2025-10-15T18:23:12.14Z" }, - { url = "https://files.pythonhosted.org/packages/0d/cd/16aec9f0da4793e98e6b54778a5fbce4f375c6646fe662e80600b8797379/pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", size = 3576812, upload-time = "2025-10-15T18:23:13.962Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b7/13957fda356dc46339298b351cae0d327704986337c3c69bb54628c88155/pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", size = 5252689, upload-time = "2025-10-15T18:23:15.562Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f5/eae31a306341d8f331f43edb2e9122c7661b975433de5e447939ae61c5da/pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", size = 4650186, upload-time = "2025-10-15T18:23:17.379Z" }, - { url = "https://files.pythonhosted.org/packages/86/62/2a88339aa40c4c77e79108facbd307d6091e2c0eb5b8d3cf4977cfca2fe6/pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", size = 6230308, upload-time = "2025-10-15T18:23:18.971Z" }, - { url = "https://files.pythonhosted.org/packages/c7/33/5425a8992bcb32d1cb9fa3dd39a89e613d09a22f2c8083b7bf43c455f760/pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", size = 8039222, upload-time = "2025-10-15T18:23:20.909Z" }, - { url = "https://files.pythonhosted.org/packages/d8/61/3f5d3b35c5728f37953d3eec5b5f3e77111949523bd2dd7f31a851e50690/pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", size = 6346657, upload-time = "2025-10-15T18:23:23.077Z" }, - { url = "https://files.pythonhosted.org/packages/3a/be/ee90a3d79271227e0f0a33c453531efd6ed14b2e708596ba5dd9be948da3/pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", size = 7038482, upload-time = "2025-10-15T18:23:25.005Z" }, - { url = "https://files.pythonhosted.org/packages/44/34/a16b6a4d1ad727de390e9bd9f19f5f669e079e5826ec0f329010ddea492f/pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", size = 6461416, upload-time = "2025-10-15T18:23:27.009Z" }, - { url = "https://files.pythonhosted.org/packages/b6/39/1aa5850d2ade7d7ba9f54e4e4c17077244ff7a2d9e25998c38a29749eb3f/pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", size = 7131584, upload-time = "2025-10-15T18:23:29.752Z" }, - { url = "https://files.pythonhosted.org/packages/bf/db/4fae862f8fad0167073a7733973bfa955f47e2cac3dc3e3e6257d10fab4a/pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", size = 6400621, upload-time = "2025-10-15T18:23:32.06Z" }, - { url = "https://files.pythonhosted.org/packages/2b/24/b350c31543fb0107ab2599464d7e28e6f856027aadda995022e695313d94/pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", size = 7142916, upload-time = "2025-10-15T18:23:34.71Z" }, - { url = "https://files.pythonhosted.org/packages/0f/9b/0ba5a6fd9351793996ef7487c4fdbde8d3f5f75dbedc093bb598648fddf0/pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", size = 2523836, upload-time = "2025-10-15T18:23:36.967Z" }, - { url = "https://files.pythonhosted.org/packages/f5/7a/ceee0840aebc579af529b523d530840338ecf63992395842e54edc805987/pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", size = 5255092, upload-time = "2025-10-15T18:23:38.573Z" }, - { url = "https://files.pythonhosted.org/packages/44/76/20776057b4bfd1aef4eeca992ebde0f53a4dce874f3ae693d0ec90a4f79b/pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", size = 4653158, upload-time = "2025-10-15T18:23:40.238Z" }, - { url = "https://files.pythonhosted.org/packages/82/3f/d9ff92ace07be8836b4e7e87e6a4c7a8318d47c2f1463ffcf121fc57d9cb/pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1", size = 6267882, upload-time = "2025-10-15T18:23:42.434Z" }, - { url = "https://files.pythonhosted.org/packages/9f/7a/4f7ff87f00d3ad33ba21af78bfcd2f032107710baf8280e3722ceec28cda/pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", size = 8071001, upload-time = "2025-10-15T18:23:44.29Z" }, - { url = "https://files.pythonhosted.org/packages/75/87/fcea108944a52dad8cca0715ae6247e271eb80459364a98518f1e4f480c1/pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", size = 6380146, upload-time = "2025-10-15T18:23:46.065Z" }, - { url = "https://files.pythonhosted.org/packages/91/52/0d31b5e571ef5fd111d2978b84603fce26aba1b6092f28e941cb46570745/pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", size = 7067344, upload-time = "2025-10-15T18:23:47.898Z" }, - { url = "https://files.pythonhosted.org/packages/7b/f4/2dd3d721f875f928d48e83bb30a434dee75a2531bca839bb996bb0aa5a91/pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", size = 6491864, upload-time = "2025-10-15T18:23:49.607Z" }, - { url = "https://files.pythonhosted.org/packages/30/4b/667dfcf3d61fc309ba5a15b141845cece5915e39b99c1ceab0f34bf1d124/pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", size = 7158911, upload-time = "2025-10-15T18:23:51.351Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2f/16cabcc6426c32218ace36bf0d55955e813f2958afddbf1d391849fee9d1/pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", size = 6408045, upload-time = "2025-10-15T18:23:53.177Z" }, - { url = "https://files.pythonhosted.org/packages/35/73/e29aa0c9c666cf787628d3f0dcf379f4791fba79f4936d02f8b37165bdf8/pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", size = 7148282, upload-time = "2025-10-15T18:23:55.316Z" }, - { url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630, upload-time = "2025-10-15T18:23:57.149Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/f2/de993bb2d21b33a98d031ecf6a978e4b61da207bef02f7b43093774c480d/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", size = 4045493 }, + { url = "https://files.pythonhosted.org/packages/0e/b6/bc8d0c4c9f6f111a783d045310945deb769b806d7574764234ffd50bc5ea/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", size = 4120461 }, + { url = "https://files.pythonhosted.org/packages/5d/57/d60d343709366a353dc56adb4ee1e7d8a2cc34e3fbc22905f4167cfec119/pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", size = 3576912 }, + { url = "https://files.pythonhosted.org/packages/a4/a4/a0a31467e3f83b94d37568294b01d22b43ae3c5d85f2811769b9c66389dd/pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", size = 5249132 }, + { url = "https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", size = 4650099 }, + { url = "https://files.pythonhosted.org/packages/fc/bd/69ed99fd46a8dba7c1887156d3572fe4484e3f031405fcc5a92e31c04035/pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", size = 6230808 }, + { url = "https://files.pythonhosted.org/packages/ea/94/8fad659bcdbf86ed70099cb60ae40be6acca434bbc8c4c0d4ef356d7e0de/pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", size = 8037804 }, + { url = "https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", size = 6345553 }, + { url = "https://files.pythonhosted.org/packages/38/57/755dbd06530a27a5ed74f8cb0a7a44a21722ebf318edbe67ddbd7fb28f88/pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", size = 7037729 }, + { url = "https://files.pythonhosted.org/packages/ca/b6/7e94f4c41d238615674d06ed677c14883103dce1c52e4af16f000338cfd7/pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", size = 6459789 }, + { url = "https://files.pythonhosted.org/packages/9c/14/4448bb0b5e0f22dd865290536d20ec8a23b64e2d04280b89139f09a36bb6/pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", size = 7130917 }, + { url = "https://files.pythonhosted.org/packages/dd/ca/16c6926cc1c015845745d5c16c9358e24282f1e588237a4c36d2b30f182f/pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", size = 6302391 }, + { url = "https://files.pythonhosted.org/packages/6d/2a/dd43dcfd6dae9b6a49ee28a8eedb98c7d5ff2de94a5d834565164667b97b/pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", size = 7007477 }, + { url = "https://files.pythonhosted.org/packages/77/f0/72ea067f4b5ae5ead653053212af05ce3705807906ba3f3e8f58ddf617e6/pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", size = 2435918 }, + { url = "https://files.pythonhosted.org/packages/f5/5e/9046b423735c21f0487ea6cb5b10f89ea8f8dfbe32576fe052b5ba9d4e5b/pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", size = 5251406 }, + { url = "https://files.pythonhosted.org/packages/12/66/982ceebcdb13c97270ef7a56c3969635b4ee7cd45227fa707c94719229c5/pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", size = 4653218 }, + { url = "https://files.pythonhosted.org/packages/16/b3/81e625524688c31859450119bf12674619429cab3119eec0e30a7a1029cb/pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", size = 6266564 }, + { url = "https://files.pythonhosted.org/packages/98/59/dfb38f2a41240d2408096e1a76c671d0a105a4a8471b1871c6902719450c/pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", size = 8069260 }, + { url = "https://files.pythonhosted.org/packages/dc/3d/378dbea5cd1874b94c312425ca77b0f47776c78e0df2df751b820c8c1d6c/pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", size = 6379248 }, + { url = "https://files.pythonhosted.org/packages/84/b0/d525ef47d71590f1621510327acec75ae58c721dc071b17d8d652ca494d8/pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", size = 7066043 }, + { url = "https://files.pythonhosted.org/packages/61/2c/aced60e9cf9d0cde341d54bf7932c9ffc33ddb4a1595798b3a5150c7ec4e/pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", size = 6490915 }, + { url = "https://files.pythonhosted.org/packages/ef/26/69dcb9b91f4e59f8f34b2332a4a0a951b44f547c4ed39d3e4dcfcff48f89/pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", size = 7157998 }, + { url = "https://files.pythonhosted.org/packages/61/2b/726235842220ca95fa441ddf55dd2382b52ab5b8d9c0596fe6b3f23dafe8/pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", size = 6306201 }, + { url = "https://files.pythonhosted.org/packages/c0/3d/2afaf4e840b2df71344ababf2f8edd75a705ce500e5dc1e7227808312ae1/pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", size = 7013165 }, + { url = "https://files.pythonhosted.org/packages/6f/75/3fa09aa5cf6ed04bee3fa575798ddf1ce0bace8edb47249c798077a81f7f/pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", size = 2437834 }, + { url = "https://files.pythonhosted.org/packages/54/2a/9a8c6ba2c2c07b71bec92cf63e03370ca5e5f5c5b119b742bcc0cde3f9c5/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", size = 4045531 }, + { url = "https://files.pythonhosted.org/packages/84/54/836fdbf1bfb3d66a59f0189ff0b9f5f666cee09c6188309300df04ad71fa/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", size = 4120554 }, + { url = "https://files.pythonhosted.org/packages/0d/cd/16aec9f0da4793e98e6b54778a5fbce4f375c6646fe662e80600b8797379/pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", size = 3576812 }, + { url = "https://files.pythonhosted.org/packages/f6/b7/13957fda356dc46339298b351cae0d327704986337c3c69bb54628c88155/pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", size = 5252689 }, + { url = "https://files.pythonhosted.org/packages/fc/f5/eae31a306341d8f331f43edb2e9122c7661b975433de5e447939ae61c5da/pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", size = 4650186 }, + { url = "https://files.pythonhosted.org/packages/86/62/2a88339aa40c4c77e79108facbd307d6091e2c0eb5b8d3cf4977cfca2fe6/pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", size = 6230308 }, + { url = "https://files.pythonhosted.org/packages/c7/33/5425a8992bcb32d1cb9fa3dd39a89e613d09a22f2c8083b7bf43c455f760/pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", size = 8039222 }, + { url = "https://files.pythonhosted.org/packages/d8/61/3f5d3b35c5728f37953d3eec5b5f3e77111949523bd2dd7f31a851e50690/pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", size = 6346657 }, + { url = "https://files.pythonhosted.org/packages/3a/be/ee90a3d79271227e0f0a33c453531efd6ed14b2e708596ba5dd9be948da3/pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", size = 7038482 }, + { url = "https://files.pythonhosted.org/packages/44/34/a16b6a4d1ad727de390e9bd9f19f5f669e079e5826ec0f329010ddea492f/pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", size = 6461416 }, + { url = "https://files.pythonhosted.org/packages/b6/39/1aa5850d2ade7d7ba9f54e4e4c17077244ff7a2d9e25998c38a29749eb3f/pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", size = 7131584 }, + { url = "https://files.pythonhosted.org/packages/bf/db/4fae862f8fad0167073a7733973bfa955f47e2cac3dc3e3e6257d10fab4a/pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", size = 6400621 }, + { url = "https://files.pythonhosted.org/packages/2b/24/b350c31543fb0107ab2599464d7e28e6f856027aadda995022e695313d94/pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", size = 7142916 }, + { url = "https://files.pythonhosted.org/packages/0f/9b/0ba5a6fd9351793996ef7487c4fdbde8d3f5f75dbedc093bb598648fddf0/pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", size = 2523836 }, + { url = "https://files.pythonhosted.org/packages/f5/7a/ceee0840aebc579af529b523d530840338ecf63992395842e54edc805987/pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", size = 5255092 }, + { url = "https://files.pythonhosted.org/packages/44/76/20776057b4bfd1aef4eeca992ebde0f53a4dce874f3ae693d0ec90a4f79b/pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", size = 4653158 }, + { url = "https://files.pythonhosted.org/packages/82/3f/d9ff92ace07be8836b4e7e87e6a4c7a8318d47c2f1463ffcf121fc57d9cb/pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1", size = 6267882 }, + { url = "https://files.pythonhosted.org/packages/9f/7a/4f7ff87f00d3ad33ba21af78bfcd2f032107710baf8280e3722ceec28cda/pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", size = 8071001 }, + { url = "https://files.pythonhosted.org/packages/75/87/fcea108944a52dad8cca0715ae6247e271eb80459364a98518f1e4f480c1/pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", size = 6380146 }, + { url = "https://files.pythonhosted.org/packages/91/52/0d31b5e571ef5fd111d2978b84603fce26aba1b6092f28e941cb46570745/pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", size = 7067344 }, + { url = "https://files.pythonhosted.org/packages/7b/f4/2dd3d721f875f928d48e83bb30a434dee75a2531bca839bb996bb0aa5a91/pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", size = 6491864 }, + { url = "https://files.pythonhosted.org/packages/30/4b/667dfcf3d61fc309ba5a15b141845cece5915e39b99c1ceab0f34bf1d124/pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", size = 7158911 }, + { url = "https://files.pythonhosted.org/packages/a2/2f/16cabcc6426c32218ace36bf0d55955e813f2958afddbf1d391849fee9d1/pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", size = 6408045 }, + { url = "https://files.pythonhosted.org/packages/35/73/e29aa0c9c666cf787628d3f0dcf379f4791fba79f4936d02f8b37165bdf8/pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", size = 7148282 }, + { url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630 }, ] [[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" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } 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" }, + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, ] [[package]] @@ -1318,36 +1327,36 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142, upload-time = "2025-03-10T15:54:38.843Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163, upload-time = "2025-03-10T15:54:37.335Z" }, + { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163 }, ] [[package]] name = "protobuf" version = "6.33.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/ff/64a6c8f420818bb873713988ca5492cba3a7946be57e027ac63495157d97/protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954", size = 443463, upload-time = "2025-10-15T20:39:52.159Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/ff/64a6c8f420818bb873713988ca5492cba3a7946be57e027ac63495157d97/protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954", size = 443463 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/ee/52b3fa8feb6db4a833dfea4943e175ce645144532e8a90f72571ad85df4e/protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035", size = 425593, upload-time = "2025-10-15T20:39:40.29Z" }, - { url = "https://files.pythonhosted.org/packages/7b/c6/7a465f1825872c55e0341ff4a80198743f73b69ce5d43ab18043699d1d81/protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee", size = 436882, upload-time = "2025-10-15T20:39:42.841Z" }, - { url = "https://files.pythonhosted.org/packages/e1/a9/b6eee662a6951b9c3640e8e452ab3e09f117d99fc10baa32d1581a0d4099/protobuf-6.33.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455", size = 427521, upload-time = "2025-10-15T20:39:43.803Z" }, - { url = "https://files.pythonhosted.org/packages/10/35/16d31e0f92c6d2f0e77c2a3ba93185130ea13053dd16200a57434c882f2b/protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90", size = 324445, upload-time = "2025-10-15T20:39:44.932Z" }, - { url = "https://files.pythonhosted.org/packages/e6/eb/2a981a13e35cda8b75b5585aaffae2eb904f8f351bdd3870769692acbd8a/protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298", size = 339159, upload-time = "2025-10-15T20:39:46.186Z" }, - { url = "https://files.pythonhosted.org/packages/21/51/0b1cbad62074439b867b4e04cc09b93f6699d78fd191bed2bbb44562e077/protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef", size = 323172, upload-time = "2025-10-15T20:39:47.465Z" }, - { url = "https://files.pythonhosted.org/packages/07/d1/0a28c21707807c6aacd5dc9c3704b2aa1effbf37adebd8caeaf68b17a636/protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995", size = 170477, upload-time = "2025-10-15T20:39:51.311Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ee/52b3fa8feb6db4a833dfea4943e175ce645144532e8a90f72571ad85df4e/protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035", size = 425593 }, + { url = "https://files.pythonhosted.org/packages/7b/c6/7a465f1825872c55e0341ff4a80198743f73b69ce5d43ab18043699d1d81/protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee", size = 436882 }, + { url = "https://files.pythonhosted.org/packages/e1/a9/b6eee662a6951b9c3640e8e452ab3e09f117d99fc10baa32d1581a0d4099/protobuf-6.33.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455", size = 427521 }, + { url = "https://files.pythonhosted.org/packages/10/35/16d31e0f92c6d2f0e77c2a3ba93185130ea13053dd16200a57434c882f2b/protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90", size = 324445 }, + { url = "https://files.pythonhosted.org/packages/e6/eb/2a981a13e35cda8b75b5585aaffae2eb904f8f351bdd3870769692acbd8a/protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298", size = 339159 }, + { url = "https://files.pythonhosted.org/packages/21/51/0b1cbad62074439b867b4e04cc09b93f6699d78fd191bed2bbb44562e077/protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef", size = 323172 }, + { url = "https://files.pythonhosted.org/packages/07/d1/0a28c21707807c6aacd5dc9c3704b2aa1effbf37adebd8caeaf68b17a636/protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995", size = 170477 }, ] [[package]] name = "psycopg" -version = "3.2.11" +version = "3.2.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/27/02/9fdfc018c026df2bcf9c11480c1014f9b90c6d801e5f929408cbfbf94cc0/psycopg-3.2.11.tar.gz", hash = "sha256:398bb484ed44361e041c8f804ed7af3d2fcefbffdace1d905b7446c319321706", size = 160644, upload-time = "2025-10-18T22:48:28.136Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/77/c72d10262b872617e509a0c60445afcc4ce2cd5cd6bc1c97700246d69c85/psycopg-3.2.12.tar.gz", hash = "sha256:85c08d6f6e2a897b16280e0ff6406bef29b1327c045db06d21f364d7cd5da90b", size = 160642 } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/1b/96ee90ed0007d64936d9bd1bb3108d0af3cf762b4f11dbd73359f0687c3d/psycopg-3.2.11-py3-none-any.whl", hash = "sha256:217231b2b6b72fba88281b94241b2f16043ee67f81def47c52a01b72ff0c086a", size = 206766, upload-time = "2025-10-18T22:43:32.114Z" }, + { url = "https://files.pythonhosted.org/packages/c8/28/8c4f90e415411dc9c78d6ba10b549baa324659907c13f64bfe3779d4066c/psycopg-3.2.12-py3-none-any.whl", hash = "sha256:8a1611a2d4c16ae37eada46438be9029a35bb959bb50b3d0e1e93c0f3d54c9ee", size = 206765 }, ] [package.optional-dependencies] @@ -1357,36 +1366,36 @@ binary = [ [[package]] name = "psycopg-binary" -version = "3.2.11" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/93/9cea78ed3b279909f0fd6c2badb24b2361b93c875d6a7c921e26f6254044/psycopg_binary-3.2.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47f6cf8a1d02d25238bdb8741ac641ff0ec22b1c6ff6a2acd057d0da5c712842", size = 4017939, upload-time = "2025-10-18T22:45:45.114Z" }, - { url = "https://files.pythonhosted.org/packages/58/86/fc9925f500b2c140c0bb8c1f8fcd04f8c45c76d4852e87baf4c75182de8c/psycopg_binary-3.2.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91268f04380964a5e767f8102d05f1e23312ddbe848de1a9514b08b3fc57d354", size = 4090150, upload-time = "2025-10-18T22:45:50.214Z" }, - { url = "https://files.pythonhosted.org/packages/4e/10/752b698da1ca9e6c5f15d8798cb637c3615315fd2da17eee4a90cf20ee08/psycopg_binary-3.2.11-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:199f88a05dd22133eab2deb30348ef7a70c23d706c8e63fdc904234163c63517", size = 4625597, upload-time = "2025-10-18T22:45:54.638Z" }, - { url = "https://files.pythonhosted.org/packages/0a/9f/b578545c3c23484f4e234282d97ab24632a1d3cbfec64209786872e7cc8f/psycopg_binary-3.2.11-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7b3c5474dbad63bcccb8d14d4d4c7c19f1dc6f8e8c1914cbc771d261cf8eddca", size = 4720326, upload-time = "2025-10-18T22:45:59.266Z" }, - { url = "https://files.pythonhosted.org/packages/43/3b/ba548d3fe65a7d4c96e568c2188e4b665802e3cba41664945ed95d16eae9/psycopg_binary-3.2.11-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:581358e770a4536e546841b78fd0fe318added4a82443bf22d0bbe3109cf9582", size = 4411647, upload-time = "2025-10-18T22:46:04.009Z" }, - { url = "https://files.pythonhosted.org/packages/26/65/559ab485b198600e7ff70d70786ae5c89d63475ca01d43a7dda0d7c91386/psycopg_binary-3.2.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:54a30f00a51b9043048b3e7ee806ffd31fc5fbd02a20f0e69d21306ff33dc473", size = 3863037, upload-time = "2025-10-18T22:46:08.469Z" }, - { url = "https://files.pythonhosted.org/packages/8c/29/05d0b48c8bef147e8216a36a1263a309a6240dcc09a56f5b8174fa6216d2/psycopg_binary-3.2.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2a438fad4cc081b018431fde0e791b6d50201526edf39522a85164f606c39ddb", size = 3536975, upload-time = "2025-10-18T22:46:12.982Z" }, - { url = "https://files.pythonhosted.org/packages/d4/75/304e133d3ab1a49602616192edb81f603ed574f79966449105f2e200999d/psycopg_binary-3.2.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f5e7415b5d0f58edf2708842c66605092df67f3821161d861b09695fc326c4de", size = 3586213, upload-time = "2025-10-18T22:46:19.523Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/c47cce42fa3c37d439e1400eaa5eeb2ce53dc3abc84d52c8a8a9e544d945/psycopg_binary-3.2.11-cp313-cp313-win_amd64.whl", hash = "sha256:6b9632c42f76d5349e7dd50025cff02688eb760b258e891ad2c6428e7e4917d5", size = 2912997, upload-time = "2025-10-18T22:46:24.978Z" }, - { url = "https://files.pythonhosted.org/packages/85/13/728b4763ef76a688737acebfcb5ab8696b024adc49a69c86081392b0e5ba/psycopg_binary-3.2.11-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:260738ae222b41dbefd0d84cb2e150a112f90b41688630f57fdac487ab6d6f38", size = 4016962, upload-time = "2025-10-18T22:46:29.207Z" }, - { url = "https://files.pythonhosted.org/packages/9f/0f/6180149621a907c5b60a2fae87d6ee10cc13e8c9f58d8250c310634ced04/psycopg_binary-3.2.11-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c594c199869099c59c85b9f4423370b6212491fb929e7fcda0da1768761a2c2c", size = 4090614, upload-time = "2025-10-18T22:46:33.073Z" }, - { url = "https://files.pythonhosted.org/packages/f8/97/cce19bdef510b698c9036d5573b941b539ffcaa7602450da559c8a62e0c3/psycopg_binary-3.2.11-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5768a9e7d393b2edd3a28de5a6d5850d054a016ed711f7044a9072f19f5e50d5", size = 4629749, upload-time = "2025-10-18T22:46:37.415Z" }, - { url = "https://files.pythonhosted.org/packages/93/9d/9bff18989fb2bf05d18c1431dd8bec4a1d90141beb11fc45d3269947ddf3/psycopg_binary-3.2.11-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:27eb6367350b75fef882c40cd6f748bfd976db2f8651f7511956f11efc15154f", size = 4724035, upload-time = "2025-10-18T22:46:42.568Z" }, - { url = "https://files.pythonhosted.org/packages/08/e5/39b930323428596990367b7953197730213d3d9d07bcedcad1d026608178/psycopg_binary-3.2.11-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa2aa5094dc962967ca0978c035b3ef90329b802501ef12a088d3bac6a55598e", size = 4411419, upload-time = "2025-10-18T22:46:47.745Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9c/97c25438d1e51ddc6a7f67990b4c59f94bc515114ada864804ccee27ef1b/psycopg_binary-3.2.11-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7744b4ed1f3b76fe37de7e9ef98014482fe74b6d3dfe1026cc4cfb4b4404e74f", size = 3867844, upload-time = "2025-10-18T22:46:53.328Z" }, - { url = "https://files.pythonhosted.org/packages/91/51/8c1e291cf4aa9982666f71a886aa782d990aa16853a42de545a0a9a871ef/psycopg_binary-3.2.11-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5f6f948ff1cd252003ff534d7b50a2b25453b4212b283a7514ff8751bdb68c37", size = 3541539, upload-time = "2025-10-18T22:46:58.993Z" }, - { url = "https://files.pythonhosted.org/packages/57/0a/e25edcdfa1111bfc5c95668b7469b5a957b40ce10cc81383688d65564826/psycopg_binary-3.2.11-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3bd2c8fb1dec6f93383fbaa561591fa3d676e079f9cb9889af17c3020a19715f", size = 3588090, upload-time = "2025-10-18T22:47:04.105Z" }, - { url = "https://files.pythonhosted.org/packages/a3/aa/f8c2f4b4c13d5680a20e5bfcd61f9e154bce26e7a2c70cb0abeade088d61/psycopg_binary-3.2.11-cp314-cp314-win_amd64.whl", hash = "sha256:c45f61202e5691090a697e599997eaffa3ec298209743caa4fd346145acabafe", size = 3006049, upload-time = "2025-10-18T22:47:07.923Z" }, +version = "3.2.12" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/0b/9d480aba4a4864832c29e6fc94ddd34d9927c276448eb3b56ffe24ed064c/psycopg_binary-3.2.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:442f20153415f374ae5753ca618637611a41a3c58c56d16ce55f845d76a3cf7b", size = 4017829 }, + { url = "https://files.pythonhosted.org/packages/a4/f3/0d294b30349bde24a46741a1f27a10e8ab81e9f4118d27c2fe592acfb42a/psycopg_binary-3.2.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:79de3cc5adbf51677009a8fda35ac9e9e3686d5595ab4b0c43ec7099ece6aeb5", size = 4089835 }, + { url = "https://files.pythonhosted.org/packages/82/d4/ff82e318e5a55d6951b278d3af7b4c7c1b19344e3a3722b6613f156a38ea/psycopg_binary-3.2.12-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:095ccda59042a1239ac2fefe693a336cb5cecf8944a8d9e98b07f07e94e2b78d", size = 4625474 }, + { url = "https://files.pythonhosted.org/packages/b1/e8/2c9df6475a5ab6d614d516f4497c568d84f7d6c21d0e11444468c9786c9f/psycopg_binary-3.2.12-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:efab679a2c7d1bf7d0ec0e1ecb47fe764945eff75bb4321f2e699b30a12db9b3", size = 4720350 }, + { url = "https://files.pythonhosted.org/packages/74/f5/7aec81b0c41985dc006e2d5822486ad4b7c2a1a97a5a05e37dc2adaf1512/psycopg_binary-3.2.12-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d369e79ad9647fc8217cbb51bbbf11f9a1ffca450be31d005340157ffe8e91b3", size = 4411621 }, + { url = "https://files.pythonhosted.org/packages/fc/15/d3cb41b8fa9d5f14320ab250545fbb66f9ddb481e448e618902672a806c0/psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eedc410f82007038030650aa58f620f9fe0009b9d6b04c3dc71cbd3bae5b2675", size = 3863081 }, + { url = "https://files.pythonhosted.org/packages/69/8a/72837664e63e3cd3aa145cedcf29e5c21257579739aba78ab7eb668f7d9c/psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f3bae4be7f6781bf6c9576eedcd5e1bb74468126fa6de991e47cdb1a8ea3a42a", size = 3537428 }, + { url = "https://files.pythonhosted.org/packages/cc/7e/1b78ae38e7d69e6d7fb1e2dcce101493f5fa429480bac3a68b876c9b1635/psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8ffe75fe6be902dadd439adf4228c98138a992088e073ede6dd34e7235f4e03e", size = 3585981 }, + { url = "https://files.pythonhosted.org/packages/a3/f8/245b4868b2dac46c3fb6383b425754ae55df1910c826d305ed414da03777/psycopg_binary-3.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:2598d0e4f2f258da13df0560187b3f1dfc9b8688c46b9d90176360ae5212c3fc", size = 2912929 }, + { url = "https://files.pythonhosted.org/packages/5c/5b/76fbb40b981b73b285a00dccafc38cf67b7a9b3f7d4f2025dda7b896e7ef/psycopg_binary-3.2.12-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dc68094e00a5a7e8c20de1d3a0d5e404a27f522e18f8eb62bbbc9f865c3c81ef", size = 4016868 }, + { url = "https://files.pythonhosted.org/packages/0e/08/8841ae3e2d1a3228e79eaaf5b7f991d15f0a231bb5031a114305b19724b1/psycopg_binary-3.2.12-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2d55009eeddbef54c711093c986daaf361d2c4210aaa1ee905075a3b97a62441", size = 4090508 }, + { url = "https://files.pythonhosted.org/packages/05/de/a41f62230cf4095ae4547eceada218cf28c17e7f94376913c1c8dde9546f/psycopg_binary-3.2.12-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:66a031f22e4418016990446d3e38143826f03ad811b9f78f58e2afbc1d343f7a", size = 4629788 }, + { url = "https://files.pythonhosted.org/packages/45/19/529d92134eae44475f781a86d58cdf3edd0953e17c69762abf387a9f2636/psycopg_binary-3.2.12-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:58ed30d33c25d7dc8d2f06285e88493147c2a660cc94713e4b563a99efb80a1f", size = 4724124 }, + { url = "https://files.pythonhosted.org/packages/5c/f5/97344e87065f7c9713ce213a2cff7732936ec3af6622e4b2a88715a953f2/psycopg_binary-3.2.12-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e0b5ccd03ca4749b8f66f38608ccbcb415cbd130d02de5eda80d042b83bee90e", size = 4411340 }, + { url = "https://files.pythonhosted.org/packages/b1/c2/34bce068f6bfb4c2e7bb1187bb64a3f3be254702b158c4ad05eacc0055cf/psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:909de94de7dd4d6086098a5755562207114c9638ec42c52d84c8a440c45fe084", size = 3867815 }, + { url = "https://files.pythonhosted.org/packages/d1/a1/c647e01ab162e6bfa52380e23e486215e9d28ffd31e9cf3cb1e9ca59008b/psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:7130effd0517881f3a852eff98729d51034128f0737f64f0d1c7ea8343d77bd7", size = 3541756 }, + { url = "https://files.pythonhosted.org/packages/6b/d0/795bdaa8c946a7b7126bf7ca8d4371eaaa613093e3ec341a0e50f52cbee2/psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:89b3c5201ca616d69ca0c3c0003ca18f7170a679c445c7e386ebfb4f29aa738e", size = 3587950 }, + { url = "https://files.pythonhosted.org/packages/53/cf/10c3e95827a3ca8af332dfc471befec86e15a14dc83cee893c49a4910dad/psycopg_binary-3.2.12-cp314-cp314-win_amd64.whl", hash = "sha256:48a8e29f3e38fcf8d393b8fe460d83e39c107ad7e5e61cd3858a7569e0554a39", size = 3005787 }, ] [[package]] name = "pwdlib" version = "0.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/a0/9daed437a6226f632a25d98d65d60ba02bdafa920c90dcb6454c611ead6c/pwdlib-0.2.1.tar.gz", hash = "sha256:9a1d8a8fa09a2f7ebf208265e55d7d008103cbdc82b9e4902ffdd1ade91add5e", size = 11699, upload-time = "2024-08-19T06:48:59.58Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/a0/9daed437a6226f632a25d98d65d60ba02bdafa920c90dcb6454c611ead6c/pwdlib-0.2.1.tar.gz", hash = "sha256:9a1d8a8fa09a2f7ebf208265e55d7d008103cbdc82b9e4902ffdd1ade91add5e", size = 11699 } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/f3/0dae5078a486f0fdf4d4a1121e103bc42694a9da9bea7b0f2c63f29cfbd3/pwdlib-0.2.1-py3-none-any.whl", hash = "sha256:1823dc6f22eae472b540e889ecf57fd424051d6a4023ec0bcf7f0de2d9d7ef8c", size = 8082, upload-time = "2024-08-19T06:49:00.997Z" }, + { url = "https://files.pythonhosted.org/packages/01/f3/0dae5078a486f0fdf4d4a1121e103bc42694a9da9bea7b0f2c63f29cfbd3/pwdlib-0.2.1-py3-none-any.whl", hash = "sha256:1823dc6f22eae472b540e889ecf57fd424051d6a4023ec0bcf7f0de2d9d7ef8c", size = 8082 }, ] [package.optional-dependencies] @@ -1401,9 +1410,9 @@ bcrypt = [ name = "pyasn1" version = "0.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 }, ] [[package]] @@ -1413,18 +1422,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892 } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259 }, ] [[package]] name = "pycparser" version = "2.23" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140 }, ] [[package]] @@ -1437,9 +1446,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/54/ecab642b3bed45f7d5f59b38443dcb36ef50f85af192e6ece103dbfe9587/pydantic-2.11.10.tar.gz", hash = "sha256:dc280f0982fbda6c38fada4e476dc0a4f3aeaf9c6ad4c28df68a666ec3c61423", size = 788494, upload-time = "2025-10-04T10:40:41.338Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/54/ecab642b3bed45f7d5f59b38443dcb36ef50f85af192e6ece103dbfe9587/pydantic-2.11.10.tar.gz", hash = "sha256:dc280f0982fbda6c38fada4e476dc0a4f3aeaf9c6ad4c28df68a666ec3c61423", size = 788494 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/1f/73c53fcbfb0b5a78f91176df41945ca466e71e9d9d836e5c522abda39ee7/pydantic-2.11.10-py3-none-any.whl", hash = "sha256:802a655709d49bd004c31e865ef37da30b540786a46bfce02333e0e24b5fe29a", size = 444823, upload-time = "2025-10-04T10:40:39.055Z" }, + { url = "https://files.pythonhosted.org/packages/bd/1f/73c53fcbfb0b5a78f91176df41945ca466e71e9d9d836e5c522abda39ee7/pydantic-2.11.10-py3-none-any.whl", hash = "sha256:802a655709d49bd004c31e865ef37da30b540786a46bfce02333e0e24b5fe29a", size = 444823 }, ] [package.optional-dependencies] @@ -1454,25 +1463,25 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, - { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, - { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, - { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, - { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, - { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, - { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, - { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, - { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, - { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, - { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, - { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, ] [[package]] @@ -1483,9 +1492,9 @@ dependencies = [ { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/10/fb64987804cde41bcc39d9cd757cd5f2bb5d97b389d81aa70238b14b8a7e/pydantic_extra_types-2.10.6.tar.gz", hash = "sha256:c63d70bf684366e6bbe1f4ee3957952ebe6973d41e7802aea0b770d06b116aeb", size = 141858, upload-time = "2025-10-08T13:47:49.483Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/10/fb64987804cde41bcc39d9cd757cd5f2bb5d97b389d81aa70238b14b8a7e/pydantic_extra_types-2.10.6.tar.gz", hash = "sha256:c63d70bf684366e6bbe1f4ee3957952ebe6973d41e7802aea0b770d06b116aeb", size = 141858 } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/04/5c918669096da8d1c9ec7bb716bd72e755526103a61bc5e76a3e4fb23b53/pydantic_extra_types-2.10.6-py3-none-any.whl", hash = "sha256:6106c448316d30abf721b5b9fecc65e983ef2614399a24142d689c7546cc246a", size = 40949, upload-time = "2025-10-08T13:47:48.268Z" }, + { url = "https://files.pythonhosted.org/packages/93/04/5c918669096da8d1c9ec7bb716bd72e755526103a61bc5e76a3e4fb23b53/pydantic_extra_types-2.10.6-py3-none-any.whl", hash = "sha256:6106c448316d30abf721b5b9fecc65e983ef2614399a24142d689c7546cc246a", size = 40949 }, ] [[package]] @@ -1497,9 +1506,9 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/c5/dbbc27b814c71676593d1c3f718e6cd7d4f00652cefa24b75f7aa3efb25e/pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180", size = 188394, upload-time = "2025-09-24T14:19:11.764Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/c5/dbbc27b814c71676593d1c3f718e6cd7d4f00652cefa24b75f7aa3efb25e/pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180", size = 188394 } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/d6/887a1ff844e64aa823fb4905978d882a633cfe295c32eacad582b78a7d8b/pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c", size = 48608, upload-time = "2025-09-24T14:19:10.015Z" }, + { url = "https://files.pythonhosted.org/packages/83/d6/887a1ff844e64aa823fb4905978d882a633cfe295c32eacad582b78a7d8b/pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c", size = 48608 }, ] [[package]] @@ -1509,27 +1518,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyparsing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/dd/e0e6a4fb84c22050f6a9701ad9fd6a67ef82faa7ba97b97eb6fdc6b49b34/pydot-3.0.4.tar.gz", hash = "sha256:3ce88b2558f3808b0376f22bfa6c263909e1c3981e2a7b629b65b451eee4a25d", size = 168167, upload-time = "2025-01-05T16:18:45.763Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/dd/e0e6a4fb84c22050f6a9701ad9fd6a67ef82faa7ba97b97eb6fdc6b49b34/pydot-3.0.4.tar.gz", hash = "sha256:3ce88b2558f3808b0376f22bfa6c263909e1c3981e2a7b629b65b451eee4a25d", size = 168167 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/5f/1ebfd430df05c4f9e438dd3313c4456eab937d976f6ab8ce81a98f9fb381/pydot-3.0.4-py3-none-any.whl", hash = "sha256:bfa9c3fc0c44ba1d132adce131802d7df00429d1a79cc0346b0a5cd374dbe9c6", size = 35776, upload-time = "2025-01-05T16:18:42.836Z" }, + { url = "https://files.pythonhosted.org/packages/b0/5f/1ebfd430df05c4f9e438dd3313c4456eab937d976f6ab8ce81a98f9fb381/pydot-3.0.4-py3-none-any.whl", hash = "sha256:bfa9c3fc0c44ba1d132adce131802d7df00429d1a79cc0346b0a5cd374dbe9c6", size = 35776 }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, ] [[package]] name = "pyjwt" version = "2.10.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785 } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, + { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997 }, ] [package.optional-dependencies] @@ -1541,31 +1550,31 @@ crypto = [ name = "pyparsing" version = "3.2.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274, upload-time = "2025-09-21T04:11:06.277Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274 } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" }, + { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890 }, ] [[package]] name = "pyreadline3" version = "3.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839, upload-time = "2024-09-19T02:40:10.062Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" }, + { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178 }, ] [[package]] name = "pyright" -version = "1.1.406" +version = "1.1.407" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f7/16/6b4fbdd1fef59a0292cbb99f790b44983e390321eccbc5921b4d161da5d1/pyright-1.1.406.tar.gz", hash = "sha256:c4872bc58c9643dac09e8a2e74d472c62036910b3bd37a32813989ef7576ea2c", size = 4113151, upload-time = "2025-10-02T01:04:45.488Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/1b/0aa08ee42948b61745ac5b5b5ccaec4669e8884b53d31c8ec20b2fcd6b6f/pyright-1.1.407.tar.gz", hash = "sha256:099674dba5c10489832d4a4b2d302636152a9a42d317986c38474c76fe562262", size = 4122872 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/a2/e309afbb459f50507103793aaef85ca4348b66814c86bc73908bdeb66d12/pyright-1.1.406-py3-none-any.whl", hash = "sha256:1d81fb43c2407bf566e97e57abb01c811973fdb21b2df8df59f870f688bdca71", size = 5980982, upload-time = "2025-10-02T01:04:43.137Z" }, + { url = "https://files.pythonhosted.org/packages/dc/93/b69052907d032b00c40cb656d21438ec00b3a471733de137a3f65a49a0a0/pyright-1.1.407-py3-none-any.whl", hash = "sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21", size = 5997008 }, ] [[package]] @@ -1579,9 +1588,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750 }, ] [[package]] @@ -1593,9 +1602,9 @@ dependencies = [ { name = "pytest" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f9/37/ad095d92242fe5c6b4b793191240375c01f6508960f31179de7f0e22cb96/pytest_alembic-0.12.1.tar.gz", hash = "sha256:4e2b477d93464d0cfe80487fdf63922bfd22f29153ca980c1bccf1dbf833cf12", size = 30635, upload-time = "2025-05-27T14:15:29.85Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/37/ad095d92242fe5c6b4b793191240375c01f6508960f31179de7f0e22cb96/pytest_alembic-0.12.1.tar.gz", hash = "sha256:4e2b477d93464d0cfe80487fdf63922bfd22f29153ca980c1bccf1dbf833cf12", size = 30635 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/f4/ded73992f972360adf84781b7e58729a3778e4358d482e1fe375c83948b4/pytest_alembic-0.12.1-py3-none-any.whl", hash = "sha256:d0d6be79f1c597278fbeda08c5558e7b8770af099521b0aa164e0df4aed945da", size = 36571, upload-time = "2025-05-27T14:15:28.817Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f4/ded73992f972360adf84781b7e58729a3778e4358d482e1fe375c83948b4/pytest_alembic-0.12.1-py3-none-any.whl", hash = "sha256:d0d6be79f1c597278fbeda08c5558e7b8770af099521b0aa164e0df4aed945da", size = 36571 }, ] [[package]] @@ -1605,9 +1614,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119 } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095 }, ] [[package]] @@ -1619,9 +1628,9 @@ dependencies = [ { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424 }, ] [[package]] @@ -1631,27 +1640,27 @@ 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" } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } 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" }, + { 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 }, ] [[package]] name = "python-dotenv" -version = "1.1.1" +version = "1.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230 }, ] [[package]] name = "python-multipart" version = "0.0.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, + { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, ] [[package]] @@ -1661,63 +1670,127 @@ 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" } +sdist = { url = "https://files.pythonhosted.org/packages/87/c7/5e1547c44e31da50a460df93af11a535ace568ef89d7a811069ead340c4a/python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856", size = 10921 } 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" }, + { url = "https://files.pythonhosted.org/packages/a4/62/02da182e544a51a5c3ccf4b03ab79df279f9c60c5e82d5e8bec7ca26ac11/python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", size = 10051 }, ] [[package]] name = "pytz" version = "2025.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, ] [[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/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" }, +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } +wheels = [ + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 }, ] [[package]] name = "redis" version = "7.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/8f/f125feec0b958e8d22c8f0b492b30b1991d9499a4315dfde466cf4289edc/redis-7.0.1.tar.gz", hash = "sha256:c949df947dca995dc68fdf5a7863950bf6df24f8d6022394585acc98e81624f1", size = 4755322, upload-time = "2025-10-27T14:34:00.33Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/97/9f22a33c475cda519f20aba6babb340fb2f2254a02fb947816960d1e669a/redis-7.0.1-py3-none-any.whl", hash = "sha256:4977af3c7d67f8f0eb8b6fec0dafc9605db9343142f634041fb0235f67c0588a", size = 339938, upload-time = "2025-10-27T14:33:58.553Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/57/8f/f125feec0b958e8d22c8f0b492b30b1991d9499a4315dfde466cf4289edc/redis-7.0.1.tar.gz", hash = "sha256:c949df947dca995dc68fdf5a7863950bf6df24f8d6022394585acc98e81624f1", size = 4755322 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/97/9f22a33c475cda519f20aba6babb340fb2f2254a02fb947816960d1e669a/redis-7.0.1-py3-none-any.whl", hash = "sha256:4977af3c7d67f8f0eb8b6fec0dafc9605db9343142f634041fb0235f67c0588a", size = 339938 }, +] + +[[package]] +name = "regex" +version = "2025.11.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/a9/546676f25e573a4cf00fe8e119b78a37b6a8fe2dc95cda877b30889c9c45/regex-2025.11.3.tar.gz", hash = "sha256:1fedc720f9bb2494ce31a58a1631f9c82df6a09b49c19517ea5cc280b4541e01", size = 414669 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/a7/dda24ebd49da46a197436ad96378f17df30ceb40e52e859fc42cac45b850/regex-2025.11.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c1e448051717a334891f2b9a620fe36776ebf3dd8ec46a0b877c8ae69575feb4", size = 489081 }, + { url = "https://files.pythonhosted.org/packages/19/22/af2dc751aacf88089836aa088a1a11c4f21a04707eb1b0478e8e8fb32847/regex-2025.11.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b5aca4d5dfd7fbfbfbdaf44850fcc7709a01146a797536a8f84952e940cca76", size = 291123 }, + { url = "https://files.pythonhosted.org/packages/a3/88/1a3ea5672f4b0a84802ee9891b86743438e7c04eb0b8f8c4e16a42375327/regex-2025.11.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:04d2765516395cf7dda331a244a3282c0f5ae96075f728629287dfa6f76ba70a", size = 288814 }, + { url = "https://files.pythonhosted.org/packages/fb/8c/f5987895bf42b8ddeea1b315c9fedcfe07cadee28b9c98cf50d00adcb14d/regex-2025.11.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d9903ca42bfeec4cebedba8022a7c97ad2aab22e09573ce9976ba01b65e4361", size = 798592 }, + { url = "https://files.pythonhosted.org/packages/99/2a/6591ebeede78203fa77ee46a1c36649e02df9eaa77a033d1ccdf2fcd5d4e/regex-2025.11.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:639431bdc89d6429f6721625e8129413980ccd62e9d3f496be618a41d205f160", size = 864122 }, + { url = "https://files.pythonhosted.org/packages/94/d6/be32a87cf28cf8ed064ff281cfbd49aefd90242a83e4b08b5a86b38e8eb4/regex-2025.11.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f117efad42068f9715677c8523ed2be1518116d1c49b1dd17987716695181efe", size = 912272 }, + { url = "https://files.pythonhosted.org/packages/62/11/9bcef2d1445665b180ac7f230406ad80671f0fc2a6ffb93493b5dd8cd64c/regex-2025.11.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4aecb6f461316adf9f1f0f6a4a1a3d79e045f9b71ec76055a791affa3b285850", size = 803497 }, + { url = "https://files.pythonhosted.org/packages/e5/a7/da0dc273d57f560399aa16d8a68ae7f9b57679476fc7ace46501d455fe84/regex-2025.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3b3a5f320136873cc5561098dfab677eea139521cb9a9e8db98b7e64aef44cbc", size = 787892 }, + { url = "https://files.pythonhosted.org/packages/da/4b/732a0c5a9736a0b8d6d720d4945a2f1e6f38f87f48f3173559f53e8d5d82/regex-2025.11.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:75fa6f0056e7efb1f42a1c34e58be24072cb9e61a601340cc1196ae92326a4f9", size = 858462 }, + { url = "https://files.pythonhosted.org/packages/0c/f5/a2a03df27dc4c2d0c769220f5110ba8c4084b0bfa9ab0f9b4fcfa3d2b0fc/regex-2025.11.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:dbe6095001465294f13f1adcd3311e50dd84e5a71525f20a10bd16689c61ce0b", size = 850528 }, + { url = "https://files.pythonhosted.org/packages/d6/09/e1cd5bee3841c7f6eb37d95ca91cdee7100b8f88b81e41c2ef426910891a/regex-2025.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:454d9b4ae7881afbc25015b8627c16d88a597479b9dea82b8c6e7e2e07240dc7", size = 789866 }, + { url = "https://files.pythonhosted.org/packages/eb/51/702f5ea74e2a9c13d855a6a85b7f80c30f9e72a95493260193c07f3f8d74/regex-2025.11.3-cp313-cp313-win32.whl", hash = "sha256:28ba4d69171fc6e9896337d4fc63a43660002b7da53fc15ac992abcf3410917c", size = 266189 }, + { url = "https://files.pythonhosted.org/packages/8b/00/6e29bb314e271a743170e53649db0fdb8e8ff0b64b4f425f5602f4eb9014/regex-2025.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:bac4200befe50c670c405dc33af26dad5a3b6b255dd6c000d92fe4629f9ed6a5", size = 277054 }, + { url = "https://files.pythonhosted.org/packages/25/f1/b156ff9f2ec9ac441710764dda95e4edaf5f36aca48246d1eea3f1fd96ec/regex-2025.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:2292cd5a90dab247f9abe892ac584cb24f0f54680c73fcb4a7493c66c2bf2467", size = 270325 }, + { url = "https://files.pythonhosted.org/packages/20/28/fd0c63357caefe5680b8ea052131acbd7f456893b69cc2a90cc3e0dc90d4/regex-2025.11.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:1eb1ebf6822b756c723e09f5186473d93236c06c579d2cc0671a722d2ab14281", size = 491984 }, + { url = "https://files.pythonhosted.org/packages/df/ec/7014c15626ab46b902b3bcc4b28a7bae46d8f281fc7ea9c95e22fcaaa917/regex-2025.11.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1e00ec2970aab10dc5db34af535f21fcf32b4a31d99e34963419636e2f85ae39", size = 292673 }, + { url = "https://files.pythonhosted.org/packages/23/ab/3b952ff7239f20d05f1f99e9e20188513905f218c81d52fb5e78d2bf7634/regex-2025.11.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a4cb042b615245d5ff9b3794f56be4138b5adc35a4166014d31d1814744148c7", size = 291029 }, + { url = "https://files.pythonhosted.org/packages/21/7e/3dc2749fc684f455f162dcafb8a187b559e2614f3826877d3844a131f37b/regex-2025.11.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44f264d4bf02f3176467d90b294d59bf1db9fe53c141ff772f27a8b456b2a9ed", size = 807437 }, + { url = "https://files.pythonhosted.org/packages/1b/0b/d529a85ab349c6a25d1ca783235b6e3eedf187247eab536797021f7126c6/regex-2025.11.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7be0277469bf3bd7a34a9c57c1b6a724532a0d235cd0dc4e7f4316f982c28b19", size = 873368 }, + { url = "https://files.pythonhosted.org/packages/7d/18/2d868155f8c9e3e9d8f9e10c64e9a9f496bb8f7e037a88a8bed26b435af6/regex-2025.11.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0d31e08426ff4b5b650f68839f5af51a92a5b51abd8554a60c2fbc7c71f25d0b", size = 914921 }, + { url = "https://files.pythonhosted.org/packages/2d/71/9d72ff0f354fa783fe2ba913c8734c3b433b86406117a8db4ea2bf1c7a2f/regex-2025.11.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e43586ce5bd28f9f285a6e729466841368c4a0353f6fd08d4ce4630843d3648a", size = 812708 }, + { url = "https://files.pythonhosted.org/packages/e7/19/ce4bf7f5575c97f82b6e804ffb5c4e940c62609ab2a0d9538d47a7fdf7d4/regex-2025.11.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0f9397d561a4c16829d4e6ff75202c1c08b68a3bdbfe29dbfcdb31c9830907c6", size = 795472 }, + { url = "https://files.pythonhosted.org/packages/03/86/fd1063a176ffb7b2315f9a1b08d17b18118b28d9df163132615b835a26ee/regex-2025.11.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:dd16e78eb18ffdb25ee33a0682d17912e8cc8a770e885aeee95020046128f1ce", size = 868341 }, + { url = "https://files.pythonhosted.org/packages/12/43/103fb2e9811205e7386366501bc866a164a0430c79dd59eac886a2822950/regex-2025.11.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:ffcca5b9efe948ba0661e9df0fa50d2bc4b097c70b9810212d6b62f05d83b2dd", size = 854666 }, + { url = "https://files.pythonhosted.org/packages/7d/22/e392e53f3869b75804762c7c848bd2dd2abf2b70fb0e526f58724638bd35/regex-2025.11.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c56b4d162ca2b43318ac671c65bd4d563e841a694ac70e1a976ac38fcf4ca1d2", size = 799473 }, + { url = "https://files.pythonhosted.org/packages/4f/f9/8bd6b656592f925b6845fcbb4d57603a3ac2fb2373344ffa1ed70aa6820a/regex-2025.11.3-cp313-cp313t-win32.whl", hash = "sha256:9ddc42e68114e161e51e272f667d640f97e84a2b9ef14b7477c53aac20c2d59a", size = 268792 }, + { url = "https://files.pythonhosted.org/packages/e5/87/0e7d603467775ff65cd2aeabf1b5b50cc1c3708556a8b849a2fa4dd1542b/regex-2025.11.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7a7c7fdf755032ffdd72c77e3d8096bdcb0eb92e89e17571a196f03d88b11b3c", size = 280214 }, + { url = "https://files.pythonhosted.org/packages/8d/d0/2afc6f8e94e2b64bfb738a7c2b6387ac1699f09f032d363ed9447fd2bb57/regex-2025.11.3-cp313-cp313t-win_arm64.whl", hash = "sha256:df9eb838c44f570283712e7cff14c16329a9f0fb19ca492d21d4b7528ee6821e", size = 271469 }, + { url = "https://files.pythonhosted.org/packages/31/e9/f6e13de7e0983837f7b6d238ad9458800a874bf37c264f7923e63409944c/regex-2025.11.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9697a52e57576c83139d7c6f213d64485d3df5bf84807c35fa409e6c970801c6", size = 489089 }, + { url = "https://files.pythonhosted.org/packages/a3/5c/261f4a262f1fa65141c1b74b255988bd2fa020cc599e53b080667d591cfc/regex-2025.11.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e18bc3f73bd41243c9b38a6d9f2366cd0e0137a9aebe2d8ff76c5b67d4c0a3f4", size = 291059 }, + { url = "https://files.pythonhosted.org/packages/8e/57/f14eeb7f072b0e9a5a090d1712741fd8f214ec193dba773cf5410108bb7d/regex-2025.11.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:61a08bcb0ec14ff4e0ed2044aad948d0659604f824cbd50b55e30b0ec6f09c73", size = 288900 }, + { url = "https://files.pythonhosted.org/packages/3c/6b/1d650c45e99a9b327586739d926a1cd4e94666b1bd4af90428b36af66dc7/regex-2025.11.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9c30003b9347c24bcc210958c5d167b9e4f9be786cb380a7d32f14f9b84674f", size = 799010 }, + { url = "https://files.pythonhosted.org/packages/99/ee/d66dcbc6b628ce4e3f7f0cbbb84603aa2fc0ffc878babc857726b8aab2e9/regex-2025.11.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4e1e592789704459900728d88d41a46fe3969b82ab62945560a31732ffc19a6d", size = 864893 }, + { url = "https://files.pythonhosted.org/packages/bf/2d/f238229f1caba7ac87a6c4153d79947fb0261415827ae0f77c304260c7d3/regex-2025.11.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6538241f45eb5a25aa575dbba1069ad786f68a4f2773a29a2bd3dd1f9de787be", size = 911522 }, + { url = "https://files.pythonhosted.org/packages/bd/3d/22a4eaba214a917c80e04f6025d26143690f0419511e0116508e24b11c9b/regex-2025.11.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce22519c989bb72a7e6b36a199384c53db7722fe669ba891da75907fe3587db", size = 803272 }, + { url = "https://files.pythonhosted.org/packages/84/b1/03188f634a409353a84b5ef49754b97dbcc0c0f6fd6c8ede505a8960a0a4/regex-2025.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:66d559b21d3640203ab9075797a55165d79017520685fb407b9234d72ab63c62", size = 787958 }, + { url = "https://files.pythonhosted.org/packages/99/6a/27d072f7fbf6fadd59c64d210305e1ff865cc3b78b526fd147db768c553b/regex-2025.11.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:669dcfb2e38f9e8c69507bace46f4889e3abbfd9b0c29719202883c0a603598f", size = 859289 }, + { url = "https://files.pythonhosted.org/packages/9a/70/1b3878f648e0b6abe023172dacb02157e685564853cc363d9961bcccde4e/regex-2025.11.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:32f74f35ff0f25a5021373ac61442edcb150731fbaa28286bbc8bb1582c89d02", size = 850026 }, + { url = "https://files.pythonhosted.org/packages/dd/d5/68e25559b526b8baab8e66839304ede68ff6727237a47727d240006bd0ff/regex-2025.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e6c7a21dffba883234baefe91bc3388e629779582038f75d2a5be918e250f0ed", size = 789499 }, + { url = "https://files.pythonhosted.org/packages/fc/df/43971264857140a350910d4e33df725e8c94dd9dee8d2e4729fa0d63d49e/regex-2025.11.3-cp314-cp314-win32.whl", hash = "sha256:795ea137b1d809eb6836b43748b12634291c0ed55ad50a7d72d21edf1cd565c4", size = 271604 }, + { url = "https://files.pythonhosted.org/packages/01/6f/9711b57dc6894a55faf80a4c1b5aa4f8649805cb9c7aef46f7d27e2b9206/regex-2025.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:9f95fbaa0ee1610ec0fc6b26668e9917a582ba80c52cc6d9ada15e30aa9ab9ad", size = 280320 }, + { url = "https://files.pythonhosted.org/packages/f1/7e/f6eaa207d4377481f5e1775cdeb5a443b5a59b392d0065f3417d31d80f87/regex-2025.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:dfec44d532be4c07088c3de2876130ff0fbeeacaa89a137decbbb5f665855a0f", size = 273372 }, + { url = "https://files.pythonhosted.org/packages/c3/06/49b198550ee0f5e4184271cee87ba4dfd9692c91ec55289e6282f0f86ccf/regex-2025.11.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ba0d8a5d7f04f73ee7d01d974d47c5834f8a1b0224390e4fe7c12a3a92a78ecc", size = 491985 }, + { url = "https://files.pythonhosted.org/packages/ce/bf/abdafade008f0b1c9da10d934034cb670432d6cf6cbe38bbb53a1cfd6cf8/regex-2025.11.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:442d86cf1cfe4faabf97db7d901ef58347efd004934da045c745e7b5bd57ac49", size = 292669 }, + { url = "https://files.pythonhosted.org/packages/f9/ef/0c357bb8edbd2ad8e273fcb9e1761bc37b8acbc6e1be050bebd6475f19c1/regex-2025.11.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fd0a5e563c756de210bb964789b5abe4f114dacae9104a47e1a649b910361536", size = 291030 }, + { url = "https://files.pythonhosted.org/packages/79/06/edbb67257596649b8fb088d6aeacbcb248ac195714b18a65e018bf4c0b50/regex-2025.11.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf3490bcbb985a1ae97b2ce9ad1c0f06a852d5b19dde9b07bdf25bf224248c95", size = 807674 }, + { url = "https://files.pythonhosted.org/packages/f4/d9/ad4deccfce0ea336296bd087f1a191543bb99ee1c53093dcd4c64d951d00/regex-2025.11.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3809988f0a8b8c9dcc0f92478d6501fac7200b9ec56aecf0ec21f4a2ec4b6009", size = 873451 }, + { url = "https://files.pythonhosted.org/packages/13/75/a55a4724c56ef13e3e04acaab29df26582f6978c000ac9cd6810ad1f341f/regex-2025.11.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f4ff94e58e84aedb9c9fce66d4ef9f27a190285b451420f297c9a09f2b9abee9", size = 914980 }, + { url = "https://files.pythonhosted.org/packages/67/1e/a1657ee15bd9116f70d4a530c736983eed997b361e20ecd8f5ca3759d5c5/regex-2025.11.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eb542fd347ce61e1321b0a6b945d5701528dca0cd9759c2e3bb8bd57e47964d", size = 812852 }, + { url = "https://files.pythonhosted.org/packages/b8/6f/f7516dde5506a588a561d296b2d0044839de06035bb486b326065b4c101e/regex-2025.11.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2d5919075a1f2e413c00b056ea0c2f065b3f5fe83c3d07d325ab92dce51d6", size = 795566 }, + { url = "https://files.pythonhosted.org/packages/d9/dd/3d10b9e170cc16fb34cb2cef91513cf3df65f440b3366030631b2984a264/regex-2025.11.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3f8bf11a4827cc7ce5a53d4ef6cddd5ad25595d3c1435ef08f76825851343154", size = 868463 }, + { url = "https://files.pythonhosted.org/packages/f5/8e/935e6beff1695aa9085ff83195daccd72acc82c81793df480f34569330de/regex-2025.11.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:22c12d837298651e5550ac1d964e4ff57c3f56965fc1812c90c9fb2028eaf267", size = 854694 }, + { url = "https://files.pythonhosted.org/packages/92/12/10650181a040978b2f5720a6a74d44f841371a3d984c2083fc1752e4acf6/regex-2025.11.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:62ba394a3dda9ad41c7c780f60f6e4a70988741415ae96f6d1bf6c239cf01379", size = 799691 }, + { url = "https://files.pythonhosted.org/packages/67/90/8f37138181c9a7690e7e4cb388debbd389342db3c7381d636d2875940752/regex-2025.11.3-cp314-cp314t-win32.whl", hash = "sha256:4bf146dca15cdd53224a1bf46d628bd7590e4a07fbb69e720d561aea43a32b38", size = 274583 }, + { url = "https://files.pythonhosted.org/packages/8f/cd/867f5ec442d56beb56f5f854f40abcfc75e11d10b11fdb1869dd39c63aaf/regex-2025.11.3-cp314-cp314t-win_amd64.whl", hash = "sha256:adad1a1bcf1c9e76346e091d22d23ac54ef28e1365117d99521631078dfec9de", size = 284286 }, + { url = "https://files.pythonhosted.org/packages/20/31/32c0c4610cbc070362bf1d2e4ea86d1ea29014d400a6d6c2486fcfd57766/regex-2025.11.3-cp314-cp314t-win_arm64.whl", hash = "sha256:c54f768482cef41e219720013cd05933b6f971d9562544d691c68699bf2b6801", size = 274741 }, ] [[package]] @@ -1789,7 +1862,7 @@ requires-dist = [ { name = "email-validator", specifier = ">=2.2.0" }, { name = "fastapi", extras = ["standard"], specifier = ">=0.115.14" }, { name = "fastapi-filter", specifier = ">=2.0.1" }, - { name = "fastapi-mail", specifier = "==1.5.2" }, + { name = "fastapi-mail", git = "https://github.com/simonvanlierde/fastapi-mail?rev=f32147ec1a450ed22262913c5ac7ec3b67dd0117" }, { name = "fastapi-pagination", specifier = ">=0.13.2" }, { name = "fastapi-storages", specifier = ">=0.3.0" }, { name = "fastapi-users", extras = ["oauth", "sqlalchemy"], specifier = ">=14.0.1" }, @@ -1847,9 +1920,9 @@ dependencies = [ { name = "pillow" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/55/1bd336b19e2483bea7d1f8c2967d25a297ddacb686ee72c13b2023ae97d0/relab_rpi_cam_models-0.1.1.tar.gz", hash = "sha256:6ac60f787b33c7951edd956c78c939764af48e63eaa9809eaa3590a604cf1dde", size = 3943, upload-time = "2025-08-19T23:38:05.058Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/55/1bd336b19e2483bea7d1f8c2967d25a297ddacb686ee72c13b2023ae97d0/relab_rpi_cam_models-0.1.1.tar.gz", hash = "sha256:6ac60f787b33c7951edd956c78c939764af48e63eaa9809eaa3590a604cf1dde", size = 3943 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/85/1ec6ec0c444dcccdc343b2978d5bcde8043b0b696c6d853c51e115d7dc53/relab_rpi_cam_models-0.1.1-py3-none-any.whl", hash = "sha256:bba3182febdbbc8f48897e1dc42ac2b779a48de8ef6be867b6131eed2b019f6b", size = 5552, upload-time = "2025-08-19T23:38:03.866Z" }, + { url = "https://files.pythonhosted.org/packages/8f/85/1ec6ec0c444dcccdc343b2978d5bcde8043b0b696c6d853c51e115d7dc53/relab_rpi_cam_models-0.1.1-py3-none-any.whl", hash = "sha256:bba3182febdbbc8f48897e1dc42ac2b779a48de8ef6be867b6131eed2b019f6b", size = 5552 }, ] [[package]] @@ -1862,9 +1935,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 }, ] [[package]] @@ -1874,9 +1947,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3c/f8/5dc70102e4d337063452c82e1f0d95e39abfe67aa222ed8a5ddeb9df8de8/requests_file-3.0.1.tar.gz", hash = "sha256:f14243d7796c588f3521bd423c5dea2ee4cc730e54a3cac9574d78aca1272576", size = 6967, upload-time = "2025-10-20T18:56:42.279Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3c/f8/5dc70102e4d337063452c82e1f0d95e39abfe67aa222ed8a5ddeb9df8de8/requests_file-3.0.1.tar.gz", hash = "sha256:f14243d7796c588f3521bd423c5dea2ee4cc730e54a3cac9574d78aca1272576", size = 6967 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/d5/de8f089119205a09da657ed4784c584ede8381a0ce6821212a6d4ca47054/requests_file-3.0.1-py2.py3-none-any.whl", hash = "sha256:d0f5eb94353986d998f80ac63c7f146a307728be051d4d1cd390dbdb59c10fa2", size = 4514, upload-time = "2025-10-20T18:56:41.184Z" }, + { url = "https://files.pythonhosted.org/packages/e1/d5/de8f089119205a09da657ed4784c584ede8381a0ce6821212a6d4ca47054/requests_file-3.0.1-py2.py3-none-any.whl", hash = "sha256:d0f5eb94353986d998f80ac63c7f146a307728be051d4d1cd390dbdb59c10fa2", size = 4514 }, ] [[package]] @@ -1887,9 +1960,9 @@ dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990 } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, + { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393 }, ] [[package]] @@ -1901,47 +1974,62 @@ dependencies = [ { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/67/33/1a18839aaa8feef7983590c05c22c9c09d245ada6017d118325bbfcc7651/rich_toolkit-0.15.1.tar.gz", hash = "sha256:6f9630eb29f3843d19d48c3bd5706a086d36d62016687f9d0efa027ddc2dd08a", size = 115322, upload-time = "2025-09-04T09:28:11.789Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/33/1a18839aaa8feef7983590c05c22c9c09d245ada6017d118325bbfcc7651/rich_toolkit-0.15.1.tar.gz", hash = "sha256:6f9630eb29f3843d19d48c3bd5706a086d36d62016687f9d0efa027ddc2dd08a", size = 115322 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/49/42821d55ead7b5a87c8d121edf323cb393d8579f63e933002ade900b784f/rich_toolkit-0.15.1-py3-none-any.whl", hash = "sha256:36a0b1d9a135d26776e4b78f1d5c2655da6e0ef432380b5c6b523c8d8ab97478", size = 29412, upload-time = "2025-09-04T09:28:10.587Z" }, + { url = "https://files.pythonhosted.org/packages/c8/49/42821d55ead7b5a87c8d121edf323cb393d8579f63e933002ade900b784f/rich_toolkit-0.15.1-py3-none-any.whl", hash = "sha256:36a0b1d9a135d26776e4b78f1d5c2655da6e0ef432380b5c6b523c8d8ab97478", size = 29412 }, ] [[package]] name = "rignore" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/1a/4e407524cf97ed42a9c77d3cc31b12dd5fb2ce542f174ff7cf78ea0ca293/rignore-0.7.1.tar.gz", hash = "sha256:67bb99d57d0bab0c473261561f98f118f7c9838a06de222338ed8f2b95ed84b4", size = 15437, upload-time = "2025-10-15T20:59:08.474Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f8/99145d7ee439db898709b9a7e913d42ed3a6ff679c50a163bae373f07276/rignore-0.7.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:cb6c993b22d7c88eeadc4fed2957be688b6c5f98d4a9b86d3a5057f4a17ea5bd", size = 881743, upload-time = "2025-10-15T20:58:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/fa/db/aea84354518a24578c77d8fec2f42c065520b48ba5bded9d8eca9e46fefd/rignore-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:32da28b0e0434b88134f8d97f22afe6bd1e2a103278a726809e2d8da8426b33f", size = 814397, upload-time = "2025-10-15T20:58:00.071Z" }, - { url = "https://files.pythonhosted.org/packages/12/0b/116afdee4093f0ccd3c4e7b6840d3699ea2a34c1ae6d1dd4d7d9d0adc65b/rignore-0.7.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:401d52a0a1c5eae342b2c7b4091206e1ce70de54e85c8c8f0ea3309765a62d60", size = 893431, upload-time = "2025-10-15T20:56:45.476Z" }, - { url = "https://files.pythonhosted.org/packages/52/b5/66778c7cbb8e2c6f4ca6f2f59067aa01632b913741c4aa46b163dc4c8f8c/rignore-0.7.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9ffcfbef75656243cfdcdd495b0ea0b71980b76af343b1bf3aed61a78db3f145", size = 867220, upload-time = "2025-10-15T20:56:58.931Z" }, - { url = "https://files.pythonhosted.org/packages/6e/da/bdd6de52941391f0056295c6904c45e1f8667df754b17fe880d0a663d941/rignore-0.7.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e89efa2ad36a9206ed30219eb1a8783a0722ae8b6d68390ae854e5f5ceab6ff", size = 1169076, upload-time = "2025-10-15T20:57:12.153Z" }, - { url = "https://files.pythonhosted.org/packages/0e/8d/d7d4bfbae28e340a6afe850809a020a31c2364fc0ee8105be4ec0841b20a/rignore-0.7.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f6191d7f52894ee65a879f022329011e31cc41f98739ff184cd3f256a3f0711", size = 937738, upload-time = "2025-10-15T20:57:25.497Z" }, - { url = "https://files.pythonhosted.org/packages/d8/b1/1d3f88aaf3cc6f4e31d1d72eb261eff3418dabd2677c83653b7574e7947a/rignore-0.7.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:873a8e84b4342534b9e283f7c17dc39c295edcdc686dfa395ddca3628316931b", size = 951791, upload-time = "2025-10-15T20:57:49.574Z" }, - { url = "https://files.pythonhosted.org/packages/90/7f/033631f29af972bc4f69e241ab188d21fbc4665ad67879c77bc984009550/rignore-0.7.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65443a6a5efd184d21538816282c78c4787a8a5f73c243ab87cbbb6f313a623d", size = 977580, upload-time = "2025-10-15T20:57:39.063Z" }, - { url = "https://files.pythonhosted.org/packages/c7/38/6f963926b769365a803ec17d448a4fc9c2dbad9c1a1bf73c28088021c2fc/rignore-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d6cafca0b422c0d57ce617fed3831e6639dc151653b98396af919f8eb3ba9e2b", size = 1074486, upload-time = "2025-10-15T20:58:18.505Z" }, - { url = "https://files.pythonhosted.org/packages/74/d2/a1c1e2cd3e43f6433d3ecb8d947e1ed684c261fa2e7b2f6b8827c3bf18d1/rignore-0.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:1f731b018b5b5a93d7b4a0f4e43e5fcbd6cf25e97cec265392f9dd8d10916e5c", size = 1131024, upload-time = "2025-10-15T20:58:32.075Z" }, - { url = "https://files.pythonhosted.org/packages/93/22/b7dd8312aa98211df1f10a6cd2a3005e72cd4ac5c125fd064c7e58394205/rignore-0.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3be78b1ab9fa1c0eac822a69a7799a2261ce06e4d548374093c4c64d796d7d8", size = 1109625, upload-time = "2025-10-15T20:58:46.077Z" }, - { url = "https://files.pythonhosted.org/packages/f7/65/dd31859304bd71ad72f71e2bf5f18e6f0043cc75394ead8c0d752ab580ad/rignore-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d8c3b77ae1a24b09a6d38e07d180f362e47b970c767d2e22417b03d95685cb9d", size = 1117466, upload-time = "2025-10-15T20:58:59.102Z" }, - { url = "https://files.pythonhosted.org/packages/5f/d7/e83241e1b0a6caef1e37586d5b2edb0227478d038675e4e6e1cd748c08ce/rignore-0.7.1-cp313-cp313-win32.whl", hash = "sha256:c01cc8c5d7099d35a7fd00e174948986d4f2cfb6b7fe2923b0b801b1a4741b37", size = 635266, upload-time = "2025-10-15T20:59:28.782Z" }, - { url = "https://files.pythonhosted.org/packages/95/e5/c2ce66a71cfc44010a238a61339cae7469adc17306025796884672784b4c/rignore-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:5dd0de4a7d38a49b9d85f332d129b4ca4a29eef5667d4c7bf503e767cf9e2ec4", size = 718048, upload-time = "2025-10-15T20:59:19.312Z" }, - { url = "https://files.pythonhosted.org/packages/ba/fb/b92aa591e247f6258997163e8b1844c9b799371fbfdfd29533e203df06b9/rignore-0.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:4a4c57b75ec758fb31ad1abab4c77810ea417e9d33bdf2f38cf9e6db556eebcb", size = 647790, upload-time = "2025-10-15T20:59:12.408Z" }, - { url = "https://files.pythonhosted.org/packages/b6/d3/b6c5764d3dcaf47de7f0e408dcb4a1a17d4ce3bb1b0aa9a346e221e3c5a1/rignore-0.7.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb7df83a41213069195436e9c1a433a6df85c089ce4be406d070a4db0ee3897", size = 892938, upload-time = "2025-10-15T20:56:46.559Z" }, - { url = "https://files.pythonhosted.org/packages/48/6a/4d8ae9af9936a061dacda0d8f638cd63571ff93e4eb28e0159db6c4dc009/rignore-0.7.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:30d9c9a93a266d1f384465d626178f49d0da4d1a0cf739f15151cdf2eb500e53", size = 867312, upload-time = "2025-10-15T20:57:00.083Z" }, - { url = "https://files.pythonhosted.org/packages/9b/88/cb243662a0b523b4350db1c7c3adee87004af90e9b26100e84c7e13b93cc/rignore-0.7.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7e83c68f557d793b4cc7aac943f3b23631469e1bc5b02e63626d0b008be01cd1", size = 1166871, upload-time = "2025-10-15T20:57:13.618Z" }, - { url = "https://files.pythonhosted.org/packages/f6/0a/da28a3f3e8ab1829180f3a7af5b601b04bab1d833e31a74fee78a2d3f5c3/rignore-0.7.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:682a6efe3f84af4b1100d4c68f0a345f490af74fd9d18346ebf67da9a3b96b08", size = 937964, upload-time = "2025-10-15T20:57:27.054Z" }, - { url = "https://files.pythonhosted.org/packages/c6/2e/f55d0759c6cf48d8fabc62d8924ce58dca81f5c370c0abdcc7cc8176210d/rignore-0.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:736b6aa3e3dfda2b1404b6f9a9d6f67e2a89f184179e9e5b629198df7c22f9c6", size = 1073720, upload-time = "2025-10-15T20:58:20.833Z" }, - { url = "https://files.pythonhosted.org/packages/c3/aa/8698caf5eb1824f8cae08cd3a296bc7f6f46e7bb539a4dd60c6a7a9f5ca2/rignore-0.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eed55292d949e99f29cd4f1ae6ddc2562428a3e74f6f4f6b8658f1d5113ffbd5", size = 1130545, upload-time = "2025-10-15T20:58:33.709Z" }, - { url = "https://files.pythonhosted.org/packages/f5/88/89abacdc122f4a0d069d12ebbd87693253f08f19457b77f030c0c6cba316/rignore-0.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:93ce054754857e37f15fe6768fd28e5450a52c7bbdb00e215100b092281ed123", size = 1108570, upload-time = "2025-10-15T20:58:47.438Z" }, - { url = "https://files.pythonhosted.org/packages/c9/4b/a815624ff1f2420ff29be1ffa2ea5204a69d9a9738fe5a6638fcd1069347/rignore-0.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:447004c774083e4f9cddf0aefcb80b12264f23e28c37918fb709917c2aabd00d", size = 1116940, upload-time = "2025-10-15T20:59:00.581Z" }, - { url = "https://files.pythonhosted.org/packages/43/63/3464fe5855fc37689d7bdd7b4b7ea0d008a8a58738bc0d68b0b5fa6dcf28/rignore-0.7.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:322ac35f2431dd2e80518200e31af1985689dfa7658003ae40012bf3d3e9f0dd", size = 880536, upload-time = "2025-10-15T20:58:11.286Z" }, - { url = "https://files.pythonhosted.org/packages/63/c3/c37469643baeb04c58db2713dc268f582974c71f3936f7d989610b344fca/rignore-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2d38e282e4b917fb6108198564b018f90de57bb6209aadf9ff39434d4709a650", size = 814741, upload-time = "2025-10-15T20:58:01.228Z" }, - { url = "https://files.pythonhosted.org/packages/76/6c/57fa917c7515db3b72a9c3a6377dc806282e6db390ace68cda29bd73774e/rignore-0.7.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89ad7373ec1e7b519a6f07dbcfca38024ba45f5e44df79ee0da4e4c817648a50", size = 951257, upload-time = "2025-10-15T20:57:50.779Z" }, - { url = "https://files.pythonhosted.org/packages/b6/58/b64fb42d6a73937a93c5f060e2720decde4d2b4a7a27fc3b69e69c397358/rignore-0.7.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ff94b215b4fe1d81e45b29dc259145fd8aaf40e7b1057f020890cd12db566e4e", size = 977468, upload-time = "2025-10-15T20:57:40.291Z" }, - { url = "https://files.pythonhosted.org/packages/22/54/5b9e60ad6ea7ef654d2607936be312ce78615e011b3461d4b1d161f031c0/rignore-0.7.1-cp314-cp314-win32.whl", hash = "sha256:f49ecef68b5cb99d1212ebe332cbb2851fb2c93672d3b1d372b0fbf475eeb172", size = 635618, upload-time = "2025-10-15T20:59:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/62/50/67137617cbe3e53cbf34d21dad49e153f731797e07261f3b00572a49e69d/rignore-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:3f55593d3bbcae3c108d546e8776e51ecb61d1d79bbb02016acf29d136813835", size = 717951, upload-time = "2025-10-15T20:59:20.519Z" }, - { url = "https://files.pythonhosted.org/packages/77/19/dd556e97354ad541b4f7f113e28503865777d6edd940c147f052dc7b8f04/rignore-0.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:60745773b5278fa5f20232fbfb148d74ad9fb27ae8a5097d3cbd5d7cc922d7f7", size = 647796, upload-time = "2025-10-15T20:59:13.724Z" }, +version = "0.7.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/8a/a4078f6e14932ac7edb171149c481de29969d96ddee3ece5dc4c26f9e0c3/rignore-0.7.6-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2bdab1d31ec9b4fb1331980ee49ea051c0d7f7bb6baa28b3125ef03cdc48fdaf", size = 883057 }, + { url = "https://files.pythonhosted.org/packages/f9/8f/f8daacd177db4bf7c2223bab41e630c52711f8af9ed279be2058d2fe4982/rignore-0.7.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:90f0a00ce0c866c275bf888271f1dc0d2140f29b82fcf33cdbda1e1a6af01010", size = 820150 }, + { url = "https://files.pythonhosted.org/packages/36/31/b65b837e39c3f7064c426754714ac633b66b8c2290978af9d7f513e14aa9/rignore-0.7.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1ad295537041dc2ed4b540fb1a3906bd9ede6ccdad3fe79770cd89e04e3c73c", size = 897406 }, + { url = "https://files.pythonhosted.org/packages/ca/58/1970ce006c427e202ac7c081435719a076c478f07b3a23f469227788dc23/rignore-0.7.6-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f782dbd3a65a5ac85adfff69e5c6b101285ef3f845c3a3cae56a54bebf9fe116", size = 874050 }, + { url = "https://files.pythonhosted.org/packages/d4/00/eb45db9f90137329072a732273be0d383cb7d7f50ddc8e0bceea34c1dfdf/rignore-0.7.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65cece3b36e5b0826d946494734c0e6aaf5a0337e18ff55b071438efe13d559e", size = 1167835 }, + { url = "https://files.pythonhosted.org/packages/f3/f1/6f1d72ddca41a64eed569680587a1236633587cc9f78136477ae69e2c88a/rignore-0.7.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d7e4bb66c13cd7602dc8931822c02dfbbd5252015c750ac5d6152b186f0a8be0", size = 941945 }, + { url = "https://files.pythonhosted.org/packages/48/6f/2f178af1c1a276a065f563ec1e11e7a9e23d4996fd0465516afce4b5c636/rignore-0.7.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297e500c15766e196f68aaaa70e8b6db85fa23fdc075b880d8231fdfba738cd7", size = 959067 }, + { url = "https://files.pythonhosted.org/packages/5b/db/423a81c4c1e173877c7f9b5767dcaf1ab50484a94f60a0b2ed78be3fa765/rignore-0.7.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a07084211a8d35e1a5b1d32b9661a5ed20669970b369df0cf77da3adea3405de", size = 984438 }, + { url = "https://files.pythonhosted.org/packages/31/eb/c4f92cc3f2825d501d3c46a244a671eb737fc1bcf7b05a3ecd34abb3e0d7/rignore-0.7.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:181eb2a975a22256a1441a9d2f15eb1292839ea3f05606620bd9e1938302cf79", size = 1078365 }, + { url = "https://files.pythonhosted.org/packages/26/09/99442f02794bd7441bfc8ed1c7319e890449b816a7493b2db0e30af39095/rignore-0.7.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:7bbcdc52b5bf9f054b34ce4af5269df5d863d9c2456243338bc193c28022bd7b", size = 1139066 }, + { url = "https://files.pythonhosted.org/packages/2c/88/bcfc21e520bba975410e9419450f4b90a2ac8236b9a80fd8130e87d098af/rignore-0.7.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f2e027a6da21a7c8c0d87553c24ca5cc4364def18d146057862c23a96546238e", size = 1118036 }, + { url = "https://files.pythonhosted.org/packages/e2/25/d37215e4562cda5c13312636393aea0bafe38d54d4e0517520a4cc0753ec/rignore-0.7.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee4a18b82cbbc648e4aac1510066682fe62beb5dc88e2c67c53a83954e541360", size = 1127550 }, + { url = "https://files.pythonhosted.org/packages/dc/76/a264ab38bfa1620ec12a8ff1c07778da89e16d8c0f3450b0333020d3d6dc/rignore-0.7.6-cp313-cp313-win32.whl", hash = "sha256:a7d7148b6e5e95035d4390396895adc384d37ff4e06781a36fe573bba7c283e5", size = 646097 }, + { url = "https://files.pythonhosted.org/packages/62/44/3c31b8983c29ea8832b6082ddb1d07b90379c2d993bd20fce4487b71b4f4/rignore-0.7.6-cp313-cp313-win_amd64.whl", hash = "sha256:b037c4b15a64dced08fc12310ee844ec2284c4c5c1ca77bc37d0a04f7bff386e", size = 726170 }, + { url = "https://files.pythonhosted.org/packages/aa/41/e26a075cab83debe41a42661262f606166157df84e0e02e2d904d134c0d8/rignore-0.7.6-cp313-cp313-win_arm64.whl", hash = "sha256:e47443de9b12fe569889bdbe020abe0e0b667516ee2ab435443f6d0869bd2804", size = 656184 }, + { url = "https://files.pythonhosted.org/packages/9a/b9/1f5bd82b87e5550cd843ceb3768b4a8ef274eb63f29333cf2f29644b3d75/rignore-0.7.6-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:8e41be9fa8f2f47239ded8920cc283699a052ac4c371f77f5ac017ebeed75732", size = 882632 }, + { url = "https://files.pythonhosted.org/packages/e9/6b/07714a3efe4a8048864e8a5b7db311ba51b921e15268b17defaebf56d3db/rignore-0.7.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6dc1e171e52cefa6c20e60c05394a71165663b48bca6c7666dee4f778f2a7d90", size = 820760 }, + { url = "https://files.pythonhosted.org/packages/ac/0f/348c829ea2d8d596e856371b14b9092f8a5dfbb62674ec9b3f67e4939a9d/rignore-0.7.6-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ce2268837c3600f82ab8db58f5834009dc638ee17103582960da668963bebc5", size = 899044 }, + { url = "https://files.pythonhosted.org/packages/f0/30/2e1841a19b4dd23878d73edd5d82e998a83d5ed9570a89675f140ca8b2ad/rignore-0.7.6-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:690a3e1b54bfe77e89c4bacb13f046e642f8baadafc61d68f5a726f324a76ab6", size = 874144 }, + { url = "https://files.pythonhosted.org/packages/c2/bf/0ce9beb2e5f64c30e3580bef09f5829236889f01511a125f98b83169b993/rignore-0.7.6-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09d12ac7a0b6210c07bcd145007117ebd8abe99c8eeb383e9e4673910c2754b2", size = 1168062 }, + { url = "https://files.pythonhosted.org/packages/b9/8b/571c178414eb4014969865317da8a02ce4cf5241a41676ef91a59aab24de/rignore-0.7.6-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a2b2b74a8c60203b08452479b90e5ce3dbe96a916214bc9eb2e5af0b6a9beb0", size = 942542 }, + { url = "https://files.pythonhosted.org/packages/19/62/7a3cf601d5a45137a7e2b89d10c05b5b86499190c4b7ca5c3c47d79ee519/rignore-0.7.6-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fc5a531ef02131e44359419a366bfac57f773ea58f5278c2cdd915f7d10ea94", size = 958739 }, + { url = "https://files.pythonhosted.org/packages/5f/1f/4261f6a0d7caf2058a5cde2f5045f565ab91aa7badc972b57d19ce58b14e/rignore-0.7.6-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7a1f77d9c4cd7e76229e252614d963442686bfe12c787a49f4fe481df49e7a9", size = 984138 }, + { url = "https://files.pythonhosted.org/packages/2b/bf/628dfe19c75e8ce1f45f7c248f5148b17dfa89a817f8e3552ab74c3ae812/rignore-0.7.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ead81f728682ba72b5b1c3d5846b011d3e0174da978de87c61645f2ed36659a7", size = 1079299 }, + { url = "https://files.pythonhosted.org/packages/af/a5/be29c50f5c0c25c637ed32db8758fdf5b901a99e08b608971cda8afb293b/rignore-0.7.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:12ffd50f520c22ffdabed8cd8bfb567d9ac165b2b854d3e679f4bcaef11a9441", size = 1139618 }, + { url = "https://files.pythonhosted.org/packages/2a/40/3c46cd7ce4fa05c20b525fd60f599165e820af66e66f2c371cd50644558f/rignore-0.7.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e5a16890fbe3c894f8ca34b0fcacc2c200398d4d46ae654e03bc9b3dbf2a0a72", size = 1117626 }, + { url = "https://files.pythonhosted.org/packages/8c/b9/aea926f263b8a29a23c75c2e0d8447965eb1879d3feb53cfcf84db67ed58/rignore-0.7.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3abab3bf99e8a77488ef6c7c9a799fac22224c28fe9f25cc21aa7cc2b72bfc0b", size = 1128144 }, + { url = "https://files.pythonhosted.org/packages/a4/f6/0d6242f8d0df7f2ecbe91679fefc1f75e7cd2072cb4f497abaab3f0f8523/rignore-0.7.6-cp314-cp314-win32.whl", hash = "sha256:eeef421c1782953c4375aa32f06ecae470c1285c6381eee2a30d2e02a5633001", size = 646385 }, + { url = "https://files.pythonhosted.org/packages/d5/38/c0dcd7b10064f084343d6af26fe9414e46e9619c5f3224b5272e8e5d9956/rignore-0.7.6-cp314-cp314-win_amd64.whl", hash = "sha256:6aeed503b3b3d5af939b21d72a82521701a4bd3b89cd761da1e7dc78621af304", size = 725738 }, + { url = "https://files.pythonhosted.org/packages/d9/7a/290f868296c1ece914d565757ab363b04730a728b544beb567ceb3b2d96f/rignore-0.7.6-cp314-cp314-win_arm64.whl", hash = "sha256:104f215b60b3c984c386c3e747d6ab4376d5656478694e22c7bd2f788ddd8304", size = 656008 }, + { url = "https://files.pythonhosted.org/packages/ca/d2/3c74e3cd81fe8ea08a8dcd2d755c09ac2e8ad8fe409508904557b58383d3/rignore-0.7.6-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bb24a5b947656dd94cb9e41c4bc8b23cec0c435b58be0d74a874f63c259549e8", size = 882835 }, + { url = "https://files.pythonhosted.org/packages/77/61/a772a34b6b63154877433ac2d048364815b24c2dd308f76b212c408101a2/rignore-0.7.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b1e33c9501cefe24b70a1eafd9821acfd0ebf0b35c3a379430a14df089993e3", size = 820301 }, + { url = "https://files.pythonhosted.org/packages/71/30/054880b09c0b1b61d17eeb15279d8bf729c0ba52b36c3ada52fb827cbb3c/rignore-0.7.6-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bec3994665a44454df86deb762061e05cd4b61e3772f5b07d1882a8a0d2748d5", size = 897611 }, + { url = "https://files.pythonhosted.org/packages/1e/40/b2d1c169f833d69931bf232600eaa3c7998ba4f9a402e43a822dad2ea9f2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26cba2edfe3cff1dfa72bddf65d316ddebf182f011f2f61538705d6dbaf54986", size = 873875 }, + { url = "https://files.pythonhosted.org/packages/55/59/ca5ae93d83a1a60e44b21d87deb48b177a8db1b85e82fc8a9abb24a8986d/rignore-0.7.6-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffa86694fec604c613696cb91e43892aa22e1fec5f9870e48f111c603e5ec4e9", size = 1167245 }, + { url = "https://files.pythonhosted.org/packages/a5/52/cf3dce392ba2af806cba265aad6bcd9c48bb2a6cb5eee448d3319f6e505b/rignore-0.7.6-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48efe2ed95aa8104145004afb15cdfa02bea5cdde8b0344afeb0434f0d989aa2", size = 941750 }, + { url = "https://files.pythonhosted.org/packages/ec/be/3f344c6218d779395e785091d05396dfd8b625f6aafbe502746fcd880af2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dcae43eb44b7f2457fef7cc87f103f9a0013017a6f4e62182c565e924948f21", size = 958896 }, + { url = "https://files.pythonhosted.org/packages/c9/34/d3fa71938aed7d00dcad87f0f9bcb02ad66c85d6ffc83ba31078ce53646a/rignore-0.7.6-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2cd649a7091c0dad2f11ef65630d30c698d505cbe8660dd395268e7c099cc99f", size = 983992 }, + { url = "https://files.pythonhosted.org/packages/24/a4/52a697158e9920705bdbd0748d59fa63e0f3233fb92e9df9a71afbead6ca/rignore-0.7.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42de84b0289d478d30ceb7ae59023f7b0527786a9a5b490830e080f0e4ea5aeb", size = 1078181 }, + { url = "https://files.pythonhosted.org/packages/ac/65/aa76dbcdabf3787a6f0fd61b5cc8ed1e88580590556d6c0207960d2384bb/rignore-0.7.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:875a617e57b53b4acbc5a91de418233849711c02e29cc1f4f9febb2f928af013", size = 1139232 }, + { url = "https://files.pythonhosted.org/packages/08/44/31b31a49b3233c6842acc1c0731aa1e7fb322a7170612acf30327f700b44/rignore-0.7.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8703998902771e96e49968105207719f22926e4431b108450f3f430b4e268b7c", size = 1117349 }, + { url = "https://files.pythonhosted.org/packages/e9/ae/1b199a2302c19c658cf74e5ee1427605234e8c91787cfba0015f2ace145b/rignore-0.7.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:602ef33f3e1b04c1e9a10a3c03f8bc3cef2d2383dcc250d309be42b49923cabc", size = 1127702 }, + { url = "https://files.pythonhosted.org/packages/fc/d3/18210222b37e87e36357f7b300b7d98c6dd62b133771e71ae27acba83a4f/rignore-0.7.6-cp314-cp314t-win32.whl", hash = "sha256:c1d8f117f7da0a4a96a8daef3da75bc090e3792d30b8b12cfadc240c631353f9", size = 647033 }, + { url = "https://files.pythonhosted.org/packages/3e/87/033eebfbee3ec7d92b3bb1717d8f68c88e6fc7de54537040f3b3a405726f/rignore-0.7.6-cp314-cp314t-win_amd64.whl", hash = "sha256:ca36e59408bec81de75d307c568c2d0d410fb880b1769be43611472c61e85c96", size = 725647 }, + { url = "https://files.pythonhosted.org/packages/79/62/b88e5879512c55b8ee979c666ee6902adc4ed05007226de266410ae27965/rignore-0.7.6-cp314-cp314t-win_arm64.whl", hash = "sha256:b83adabeb3e8cf662cabe1931b83e165b88c526fa6af6b3aa90429686e474896", size = 656035 }, ] [[package]] @@ -1951,35 +2039,35 @@ 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" } +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034 } 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" }, + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696 }, ] [[package]] name = "ruff" -version = "0.14.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/58/6ca66896635352812de66f71cdf9ff86b3a4f79071ca5730088c0cd0fc8d/ruff-0.14.1.tar.gz", hash = "sha256:1dd86253060c4772867c61791588627320abcb6ed1577a90ef432ee319729b69", size = 5513429, upload-time = "2025-10-16T18:05:41.766Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/39/9cc5ab181478d7a18adc1c1e051a84ee02bec94eb9bdfd35643d7c74ca31/ruff-0.14.1-py3-none-linux_armv6l.whl", hash = "sha256:083bfc1f30f4a391ae09c6f4f99d83074416b471775b59288956f5bc18e82f8b", size = 12445415, upload-time = "2025-10-16T18:04:48.227Z" }, - { url = "https://files.pythonhosted.org/packages/ef/2e/1226961855ccd697255988f5a2474890ac7c5863b080b15bd038df820818/ruff-0.14.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f6fa757cd717f791009f7669fefb09121cc5f7d9bd0ef211371fad68c2b8b224", size = 12784267, upload-time = "2025-10-16T18:04:52.515Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ea/fd9e95863124ed159cd0667ec98449ae461de94acda7101f1acb6066da00/ruff-0.14.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d6191903d39ac156921398e9c86b7354d15e3c93772e7dbf26c9fcae59ceccd5", size = 11781872, upload-time = "2025-10-16T18:04:55.396Z" }, - { url = "https://files.pythonhosted.org/packages/1e/5a/e890f7338ff537dba4589a5e02c51baa63020acfb7c8cbbaea4831562c96/ruff-0.14.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed04f0e04f7a4587244e5c9d7df50e6b5bf2705d75059f409a6421c593a35896", size = 12226558, upload-time = "2025-10-16T18:04:58.166Z" }, - { url = "https://files.pythonhosted.org/packages/a6/7a/8ab5c3377f5bf31e167b73651841217542bcc7aa1c19e83030835cc25204/ruff-0.14.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5c9e6cf6cd4acae0febbce29497accd3632fe2025c0c583c8b87e8dbdeae5f61", size = 12187898, upload-time = "2025-10-16T18:05:01.455Z" }, - { url = "https://files.pythonhosted.org/packages/48/8d/ba7c33aa55406955fc124e62c8259791c3d42e3075a71710fdff9375134f/ruff-0.14.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fa2458527794ecdfbe45f654e42c61f2503a230545a91af839653a0a93dbc6", size = 12939168, upload-time = "2025-10-16T18:05:04.397Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c2/70783f612b50f66d083380e68cbd1696739d88e9b4f6164230375532c637/ruff-0.14.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:39f1c392244e338b21d42ab29b8a6392a722c5090032eb49bb4d6defcdb34345", size = 14386942, upload-time = "2025-10-16T18:05:07.102Z" }, - { url = "https://files.pythonhosted.org/packages/48/44/cd7abb9c776b66d332119d67f96acf15830d120f5b884598a36d9d3f4d83/ruff-0.14.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7382fa12a26cce1f95070ce450946bec357727aaa428983036362579eadcc5cf", size = 13990622, upload-time = "2025-10-16T18:05:09.882Z" }, - { url = "https://files.pythonhosted.org/packages/eb/56/4259b696db12ac152fe472764b4f78bbdd9b477afd9bc3a6d53c01300b37/ruff-0.14.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd0bf2be3ae8521e1093a487c4aa3b455882f139787770698530d28ed3fbb37c", size = 13431143, upload-time = "2025-10-16T18:05:13.46Z" }, - { url = "https://files.pythonhosted.org/packages/e0/35/266a80d0eb97bd224b3265b9437bd89dde0dcf4faf299db1212e81824e7e/ruff-0.14.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cabcaa9ccf8089fb4fdb78d17cc0e28241520f50f4c2e88cb6261ed083d85151", size = 13132844, upload-time = "2025-10-16T18:05:16.1Z" }, - { url = "https://files.pythonhosted.org/packages/65/6e/d31ce218acc11a8d91ef208e002a31acf315061a85132f94f3df7a252b18/ruff-0.14.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:747d583400f6125ec11a4c14d1c8474bf75d8b419ad22a111a537ec1a952d192", size = 13401241, upload-time = "2025-10-16T18:05:19.395Z" }, - { url = "https://files.pythonhosted.org/packages/9f/b5/dbc4221bf0b03774b3b2f0d47f39e848d30664157c15b965a14d890637d2/ruff-0.14.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5a6e74c0efd78515a1d13acbfe6c90f0f5bd822aa56b4a6d43a9ffb2ae6e56cd", size = 12132476, upload-time = "2025-10-16T18:05:22.163Z" }, - { url = "https://files.pythonhosted.org/packages/98/4b/ac99194e790ccd092d6a8b5f341f34b6e597d698e3077c032c502d75ea84/ruff-0.14.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0ea6a864d2fb41a4b6d5b456ed164302a0d96f4daac630aeba829abfb059d020", size = 12139749, upload-time = "2025-10-16T18:05:25.162Z" }, - { url = "https://files.pythonhosted.org/packages/47/26/7df917462c3bb5004e6fdfcc505a49e90bcd8a34c54a051953118c00b53a/ruff-0.14.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0826b8764f94229604fa255918d1cc45e583e38c21c203248b0bfc9a0e930be5", size = 12544758, upload-time = "2025-10-16T18:05:28.018Z" }, - { url = "https://files.pythonhosted.org/packages/64/d0/81e7f0648e9764ad9b51dd4be5e5dac3fcfff9602428ccbae288a39c2c22/ruff-0.14.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cbc52160465913a1a3f424c81c62ac8096b6a491468e7d872cb9444a860bc33d", size = 13221811, upload-time = "2025-10-16T18:05:30.707Z" }, - { url = "https://files.pythonhosted.org/packages/c3/07/3c45562c67933cc35f6d5df4ca77dabbcd88fddaca0d6b8371693d29fd56/ruff-0.14.1-py3-none-win32.whl", hash = "sha256:e037ea374aaaff4103240ae79168c0945ae3d5ae8db190603de3b4012bd1def6", size = 12319467, upload-time = "2025-10-16T18:05:33.261Z" }, - { url = "https://files.pythonhosted.org/packages/02/88/0ee4ca507d4aa05f67e292d2e5eb0b3e358fbcfe527554a2eda9ac422d6b/ruff-0.14.1-py3-none-win_amd64.whl", hash = "sha256:59d599cdff9c7f925a017f6f2c256c908b094e55967f93f2821b1439928746a1", size = 13401123, upload-time = "2025-10-16T18:05:35.984Z" }, - { url = "https://files.pythonhosted.org/packages/b8/81/4b6387be7014858d924b843530e1b2a8e531846807516e9bea2ee0936bf7/ruff-0.14.1-py3-none-win_arm64.whl", hash = "sha256:e3b443c4c9f16ae850906b8d0a707b2a4c16f8d2f0a7fe65c475c5886665ce44", size = 12436636, upload-time = "2025-10-16T18:05:38.995Z" }, +version = "0.14.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/55/cccfca45157a2031dcbb5a462a67f7cf27f8b37d4b3b1cd7438f0f5c1df6/ruff-0.14.4.tar.gz", hash = "sha256:f459a49fe1085a749f15414ca76f61595f1a2cc8778ed7c279b6ca2e1fd19df3", size = 5587844 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/b9/67240254166ae1eaa38dec32265e9153ac53645a6c6670ed36ad00722af8/ruff-0.14.4-py3-none-linux_armv6l.whl", hash = "sha256:e6604613ffbcf2297cd5dcba0e0ac9bd0c11dc026442dfbb614504e87c349518", size = 12606781 }, + { url = "https://files.pythonhosted.org/packages/46/c8/09b3ab245d8652eafe5256ab59718641429f68681ee713ff06c5c549f156/ruff-0.14.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d99c0b52b6f0598acede45ee78288e5e9b4409d1ce7f661f0fa36d4cbeadf9a4", size = 12946765 }, + { url = "https://files.pythonhosted.org/packages/14/bb/1564b000219144bf5eed2359edc94c3590dd49d510751dad26202c18a17d/ruff-0.14.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9358d490ec030f1b51d048a7fd6ead418ed0826daf6149e95e30aa67c168af33", size = 11928120 }, + { url = "https://files.pythonhosted.org/packages/a3/92/d5f1770e9988cc0742fefaa351e840d9aef04ec24ae1be36f333f96d5704/ruff-0.14.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b40d27924f1f02dfa827b9c0712a13c0e4b108421665322218fc38caf615c2", size = 12370877 }, + { url = "https://files.pythonhosted.org/packages/e2/29/e9282efa55f1973d109faf839a63235575519c8ad278cc87a182a366810e/ruff-0.14.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f5e649052a294fe00818650712083cddc6cc02744afaf37202c65df9ea52efa5", size = 12408538 }, + { url = "https://files.pythonhosted.org/packages/8e/01/930ed6ecfce130144b32d77d8d69f5c610e6d23e6857927150adf5d7379a/ruff-0.14.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa082a8f878deeba955531f975881828fd6afd90dfa757c2b0808aadb437136e", size = 13141942 }, + { url = "https://files.pythonhosted.org/packages/6a/46/a9c89b42b231a9f487233f17a89cbef9d5acd538d9488687a02ad288fa6b/ruff-0.14.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1043c6811c2419e39011890f14d0a30470f19d47d197c4858b2787dfa698f6c8", size = 14544306 }, + { url = "https://files.pythonhosted.org/packages/78/96/9c6cf86491f2a6d52758b830b89b78c2ae61e8ca66b86bf5a20af73d20e6/ruff-0.14.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a9f3a936ac27fb7c2a93e4f4b943a662775879ac579a433291a6f69428722649", size = 14210427 }, + { url = "https://files.pythonhosted.org/packages/71/f4/0666fe7769a54f63e66404e8ff698de1dcde733e12e2fd1c9c6efb689cb5/ruff-0.14.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95643ffd209ce78bc113266b88fba3d39e0461f0cbc8b55fb92505030fb4a850", size = 13658488 }, + { url = "https://files.pythonhosted.org/packages/ee/79/6ad4dda2cfd55e41ac9ed6d73ef9ab9475b1eef69f3a85957210c74ba12c/ruff-0.14.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:456daa2fa1021bc86ca857f43fe29d5d8b3f0e55e9f90c58c317c1dcc2afc7b5", size = 13354908 }, + { url = "https://files.pythonhosted.org/packages/b5/60/f0b6990f740bb15c1588601d19d21bcc1bd5de4330a07222041678a8e04f/ruff-0.14.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f911bba769e4a9f51af6e70037bb72b70b45a16db5ce73e1f72aefe6f6d62132", size = 13587803 }, + { url = "https://files.pythonhosted.org/packages/c9/da/eaaada586f80068728338e0ef7f29ab3e4a08a692f92eb901a4f06bbff24/ruff-0.14.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:76158a7369b3979fa878612c623a7e5430c18b2fd1c73b214945c2d06337db67", size = 12279654 }, + { url = "https://files.pythonhosted.org/packages/66/d4/b1d0e82cf9bf8aed10a6d45be47b3f402730aa2c438164424783ac88c0ed/ruff-0.14.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f3b8f3b442d2b14c246e7aeca2e75915159e06a3540e2f4bed9f50d062d24469", size = 12357520 }, + { url = "https://files.pythonhosted.org/packages/04/f4/53e2b42cc82804617e5c7950b7079d79996c27e99c4652131c6a1100657f/ruff-0.14.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c62da9a06779deecf4d17ed04939ae8b31b517643b26370c3be1d26f3ef7dbde", size = 12719431 }, + { url = "https://files.pythonhosted.org/packages/a2/94/80e3d74ed9a72d64e94a7b7706b1c1ebaa315ef2076fd33581f6a1cd2f95/ruff-0.14.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5a443a83a1506c684e98acb8cb55abaf3ef725078be40237463dae4463366349", size = 13464394 }, + { url = "https://files.pythonhosted.org/packages/54/1a/a49f071f04c42345c793d22f6cf5e0920095e286119ee53a64a3a3004825/ruff-0.14.4-py3-none-win32.whl", hash = "sha256:643b69cb63cd996f1fc7229da726d07ac307eae442dd8974dbc7cf22c1e18fff", size = 12493429 }, + { url = "https://files.pythonhosted.org/packages/bc/22/e58c43e641145a2b670328fb98bc384e20679b5774258b1e540207580266/ruff-0.14.4-py3-none-win_amd64.whl", hash = "sha256:26673da283b96fe35fa0c939bf8411abec47111644aa9f7cfbd3c573fb125d2c", size = 13635380 }, + { url = "https://files.pythonhosted.org/packages/30/bd/4168a751ddbbf43e86544b4de8b5c3b7be8d7167a2a5cb977d274e04f0a1/ruff-0.14.4-py3-none-win_arm64.whl", hash = "sha256:dd09c292479596b0e6fec8cd95c65c3a6dc68e9ad17b8f2382130f87ff6a75bb", size = 12663065 }, ] [[package]] @@ -1989,58 +2077,58 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/62/74/8d69dcb7a9efe8baa2046891735e5dfe433ad558ae23d9e3c14c633d1d58/s3transfer-0.14.0.tar.gz", hash = "sha256:eff12264e7c8b4985074ccce27a3b38a485bb7f7422cc8046fee9be4983e4125", size = 151547, upload-time = "2025-09-09T19:23:31.089Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/74/8d69dcb7a9efe8baa2046891735e5dfe433ad558ae23d9e3c14c633d1d58/s3transfer-0.14.0.tar.gz", hash = "sha256:eff12264e7c8b4985074ccce27a3b38a485bb7f7422cc8046fee9be4983e4125", size = 151547 } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/f0/ae7ca09223a81a1d890b2557186ea015f6e0502e9b8cb8e1813f1d8cfa4e/s3transfer-0.14.0-py3-none-any.whl", hash = "sha256:ea3b790c7077558ed1f02a3072fb3cb992bbbd253392f4b6e9e8976941c7d456", size = 85712, upload-time = "2025-09-09T19:23:30.041Z" }, + { url = "https://files.pythonhosted.org/packages/48/f0/ae7ca09223a81a1d890b2557186ea015f6e0502e9b8cb8e1813f1d8cfa4e/s3transfer-0.14.0-py3-none-any.whl", hash = "sha256:ea3b790c7077558ed1f02a3072fb3cb992bbbd253392f4b6e9e8976941c7d456", size = 85712 }, ] [[package]] name = "sentry-sdk" -version = "2.42.1" +version = "2.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/31/04/ec8c1dd9250847303d98516e917978cb1c7083024770d86d657d2ccb5a70/sentry_sdk-2.42.1.tar.gz", hash = "sha256:8598cc6edcfe74cb8074ba6a7c15338cdee93d63d3eb9b9943b4b568354ad5b6", size = 354839, upload-time = "2025-10-20T12:38:40.45Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/18/09875b4323b03ca9025bae7e6539797b27e4fc032998a466b4b9c3d24653/sentry_sdk-2.43.0.tar.gz", hash = "sha256:52ed6e251c5d2c084224d73efee56b007ef5c2d408a4a071270e82131d336e20", size = 368953 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/cb/c21b96ff379923310b4fb2c06e8d560d801e24aeb300faa72a04776868fc/sentry_sdk-2.42.1-py2.py3-none-any.whl", hash = "sha256:f8716b50c927d3beb41bc88439dc6bcd872237b596df5b14613e2ade104aee02", size = 380952, upload-time = "2025-10-20T12:38:38.88Z" }, + { url = "https://files.pythonhosted.org/packages/69/31/8228fa962f7fd8814d634e4ebece8780e2cdcfbdf0cd2e14d4a6861a7cd5/sentry_sdk-2.43.0-py2.py3-none-any.whl", hash = "sha256:4aacafcf1756ef066d359ae35030881917160ba7f6fc3ae11e0e58b09edc2d5d", size = 400997 }, ] [[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" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } 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" }, + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, ] [[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" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } 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" }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, ] [[package]] name = "sniffio" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, ] [[package]] name = "soupsieve" version = "2.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472 } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" }, + { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679 }, ] [[package]] @@ -2054,9 +2142,9 @@ dependencies = [ { name = "starlette" }, { name = "wtforms" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/0c/614041e1b544e0de1f43b58f0105b3e2795b80369d5b0ff7412882d42fff/sqladmin-0.21.0.tar.gz", hash = "sha256:cb455b79eb79ef7d904680dd83817bf7750675147400b5b7cc401d04bda7ef2c", size = 1428312, upload-time = "2025-07-02T09:41:21.207Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/0c/614041e1b544e0de1f43b58f0105b3e2795b80369d5b0ff7412882d42fff/sqladmin-0.21.0.tar.gz", hash = "sha256:cb455b79eb79ef7d904680dd83817bf7750675147400b5b7cc401d04bda7ef2c", size = 1428312 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/8d/81b2a48cc6f5479cb1148292518e3006ec8f5fbe3b0829ef165984e9d7b9/sqladmin-0.21.0-py3-none-any.whl", hash = "sha256:2b1802c49bdd3128c6452625705693cf32d5d33e7db30e63f409bd20a9c05b53", size = 1443585, upload-time = "2025-07-02T09:41:19.205Z" }, + { url = "https://files.pythonhosted.org/packages/ed/8d/81b2a48cc6f5479cb1148292518e3006ec8f5fbe3b0829ef165984e9d7b9/sqladmin-0.21.0-py3-none-any.whl", hash = "sha256:2b1802c49bdd3128c6452625705693cf32d5d33e7db30e63f409bd20a9c05b53", size = 1443585 }, ] [[package]] @@ -2067,17 +2155,17 @@ 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/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830, upload-time = "2025-10-10T14:39:12.935Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830 } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/d3/c67077a2249fdb455246e6853166360054c331db4613cda3e31ab1cadbef/sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1", size = 2135479, upload-time = "2025-10-10T16:03:37.671Z" }, - { url = "https://files.pythonhosted.org/packages/2b/91/eabd0688330d6fd114f5f12c4f89b0d02929f525e6bf7ff80aa17ca802af/sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45", size = 2123212, upload-time = "2025-10-10T16:03:41.755Z" }, - { url = "https://files.pythonhosted.org/packages/b0/bb/43e246cfe0e81c018076a16036d9b548c4cc649de241fa27d8d9ca6f85ab/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976", size = 3255353, upload-time = "2025-10-10T15:35:31.221Z" }, - { url = "https://files.pythonhosted.org/packages/b9/96/c6105ed9a880abe346b64d3b6ddef269ddfcab04f7f3d90a0bf3c5a88e82/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c", size = 3260222, upload-time = "2025-10-10T15:43:50.124Z" }, - { url = "https://files.pythonhosted.org/packages/44/16/1857e35a47155b5ad927272fee81ae49d398959cb749edca6eaa399b582f/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d", size = 3189614, upload-time = "2025-10-10T15:35:32.578Z" }, - { url = "https://files.pythonhosted.org/packages/88/ee/4afb39a8ee4fc786e2d716c20ab87b5b1fb33d4ac4129a1aaa574ae8a585/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40", size = 3226248, upload-time = "2025-10-10T15:43:51.862Z" }, - { url = "https://files.pythonhosted.org/packages/32/d5/0e66097fc64fa266f29a7963296b40a80d6a997b7ac13806183700676f86/sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73", size = 2101275, upload-time = "2025-10-10T15:03:26.096Z" }, - { url = "https://files.pythonhosted.org/packages/03/51/665617fe4f8c6450f42a6d8d69243f9420f5677395572c2fe9d21b493b7b/sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e", size = 2127901, upload-time = "2025-10-10T15:03:27.548Z" }, - { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718, upload-time = "2025-10-10T15:29:45.32Z" }, + { url = "https://files.pythonhosted.org/packages/45/d3/c67077a2249fdb455246e6853166360054c331db4613cda3e31ab1cadbef/sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1", size = 2135479 }, + { url = "https://files.pythonhosted.org/packages/2b/91/eabd0688330d6fd114f5f12c4f89b0d02929f525e6bf7ff80aa17ca802af/sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45", size = 2123212 }, + { url = "https://files.pythonhosted.org/packages/b0/bb/43e246cfe0e81c018076a16036d9b548c4cc649de241fa27d8d9ca6f85ab/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976", size = 3255353 }, + { url = "https://files.pythonhosted.org/packages/b9/96/c6105ed9a880abe346b64d3b6ddef269ddfcab04f7f3d90a0bf3c5a88e82/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c", size = 3260222 }, + { url = "https://files.pythonhosted.org/packages/44/16/1857e35a47155b5ad927272fee81ae49d398959cb749edca6eaa399b582f/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d", size = 3189614 }, + { url = "https://files.pythonhosted.org/packages/88/ee/4afb39a8ee4fc786e2d716c20ab87b5b1fb33d4ac4129a1aaa574ae8a585/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40", size = 3226248 }, + { url = "https://files.pythonhosted.org/packages/32/d5/0e66097fc64fa266f29a7963296b40a80d6a997b7ac13806183700676f86/sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73", size = 2101275 }, + { url = "https://files.pythonhosted.org/packages/03/51/665617fe4f8c6450f42a6d8d69243f9420f5677395572c2fe9d21b493b7b/sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e", size = 2127901 }, + { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718 }, ] [package.optional-dependencies] @@ -2093,30 +2181,30 @@ dependencies = [ { name = "pydantic" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/5a/693d90866233e837d182da76082a6d4c2303f54d3aaaa5c78e1238c5d863/sqlmodel-0.0.27.tar.gz", hash = "sha256:ad1227f2014a03905aef32e21428640848ac09ff793047744a73dfdd077ff620", size = 118053, upload-time = "2025-10-08T16:39:11.938Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/5a/693d90866233e837d182da76082a6d4c2303f54d3aaaa5c78e1238c5d863/sqlmodel-0.0.27.tar.gz", hash = "sha256:ad1227f2014a03905aef32e21428640848ac09ff793047744a73dfdd077ff620", size = 118053 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/92/c35e036151fe53822893979f8a13e6f235ae8191f4164a79ae60a95d66aa/sqlmodel-0.0.27-py3-none-any.whl", hash = "sha256:667fe10aa8ff5438134668228dc7d7a08306f4c5c4c7e6ad3ad68defa0e7aa49", size = 29131, upload-time = "2025-10-08T16:39:10.917Z" }, + { url = "https://files.pythonhosted.org/packages/8c/92/c35e036151fe53822893979f8a13e6f235ae8191f4164a79ae60a95d66aa/sqlmodel-0.0.27-py3-none-any.whl", hash = "sha256:667fe10aa8ff5438134668228dc7d7a08306f4c5c4c7e6ad3ad68defa0e7aa49", size = 29131 }, ] [[package]] name = "starlette" -version = "0.48.0" +version = "0.49.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a7/a5/d6f429d43394057b67a6b5bbe6eae2f77a6bf7459d961fdb224bf206eee6/starlette-0.48.0.tar.gz", hash = "sha256:7e8cee469a8ab2352911528110ce9088fdc6a37d9876926e73da7ce4aa4c7a46", size = 2652949, upload-time = "2025-09-13T08:41:05.699Z" } +sdist = { url = "https://files.pythonhosted.org/packages/de/1a/608df0b10b53b0beb96a37854ee05864d182ddd4b1156a22f1ad3860425a/starlette-0.49.3.tar.gz", hash = "sha256:1c14546f299b5901a1ea0e34410575bc33bbd741377a10484a54445588d00284", size = 2655031 } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/72/2db2f49247d0a18b4f1bb9a5a39a0162869acf235f3a96418363947b3d46/starlette-0.48.0-py3-none-any.whl", hash = "sha256:0764ca97b097582558ecb498132ed0c7d942f233f365b86ba37770e026510659", size = 73736, upload-time = "2025-09-13T08:41:03.869Z" }, + { url = "https://files.pythonhosted.org/packages/a3/e0/021c772d6a662f43b63044ab481dc6ac7592447605b5b35a957785363122/starlette-0.49.3-py3-none-any.whl", hash = "sha256:b579b99715fdc2980cf88c8ec96d3bf1ce16f5a8051a7c2b84ef9b1cdecaea2f", size = 74340 }, ] [[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" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/e2/e9a00f0ccb71718418230718b3d900e71a5d16e701a3dae079a21e9cd8f8/text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93", size = 76885 } 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" }, + { url = "https://files.pythonhosted.org/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154 }, ] [[package]] @@ -2129,9 +2217,9 @@ dependencies = [ { name = "requests" }, { name = "requests-file" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/78/182641ea38e3cfd56e9c7b3c0d48a53d432eea755003aa544af96403d4ac/tldextract-5.3.0.tar.gz", hash = "sha256:b3d2b70a1594a0ecfa6967d57251527d58e00bb5a91a74387baa0d87a0678609", size = 128502, upload-time = "2025-04-22T06:19:37.491Z" } +sdist = { url = "https://files.pythonhosted.org/packages/97/78/182641ea38e3cfd56e9c7b3c0d48a53d432eea755003aa544af96403d4ac/tldextract-5.3.0.tar.gz", hash = "sha256:b3d2b70a1594a0ecfa6967d57251527d58e00bb5a91a74387baa0d87a0678609", size = 128502 } wheels = [ - { url = "https://files.pythonhosted.org/packages/67/7c/ea488ef48f2f544566947ced88541bc45fae9e0e422b2edbf165ee07da99/tldextract-5.3.0-py3-none-any.whl", hash = "sha256:f70f31d10b55c83993f55e91ecb7c5d84532a8972f22ec578ecfbe5ea2292db2", size = 107384, upload-time = "2025-04-22T06:19:36.304Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/ea488ef48f2f544566947ced88541bc45fae9e0e422b2edbf165ee07da99/tldextract-5.3.0-py3-none-any.whl", hash = "sha256:f70f31d10b55c83993f55e91ecb7c5d84532a8972f22ec578ecfbe5ea2292db2", size = 107384 }, ] [[package]] @@ -2144,18 +2232,18 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8f/28/7c85c8032b91dbe79725b6f17d2fffc595dff06a35c7a30a37bef73a1ab4/typer-0.20.0.tar.gz", hash = "sha256:1aaf6494031793e4876fb0bacfa6a912b551cf43c1e63c800df8b1a866720c37", size = 106492, upload-time = "2025-10-20T17:03:49.445Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8f/28/7c85c8032b91dbe79725b6f17d2fffc595dff06a35c7a30a37bef73a1ab4/typer-0.20.0.tar.gz", hash = "sha256:1aaf6494031793e4876fb0bacfa6a912b551cf43c1e63c800df8b1a866720c37", size = 106492 } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/64/7713ffe4b5983314e9d436a90d5bd4f63b6054e2aca783a3cfc44cb95bbf/typer-0.20.0-py3-none-any.whl", hash = "sha256:5b463df6793ec1dca6213a3cf4c0f03bc6e322ac5e16e13ddd622a889489784a", size = 47028, upload-time = "2025-10-20T17:03:47.617Z" }, + { url = "https://files.pythonhosted.org/packages/78/64/7713ffe4b5983314e9d436a90d5bd4f63b6054e2aca783a3cfc44cb95bbf/typer-0.20.0-py3-none-any.whl", hash = "sha256:5b463df6793ec1dca6213a3cf4c0f03bc6e322ac5e16e13ddd622a889489784a", size = 47028 }, ] [[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" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } 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" }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, ] [[package]] @@ -2165,36 +2253,36 @@ 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" } +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949 } 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" }, + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 }, ] [[package]] name = "tzdata" version = "2025.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839 }, ] [[package]] name = "uritemplate" version = "4.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/60/f174043244c5306c9988380d2cb10009f91563fc4b31293d27e17201af56/uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e", size = 33267, upload-time = "2025-06-02T15:12:06.318Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/60/f174043244c5306c9988380d2cb10009f91563fc4b31293d27e17201af56/uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e", size = 33267 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/99/3ae339466c9183ea5b8ae87b34c0b897eda475d2aec2307cae60e5cd4f29/uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686", size = 11488, upload-time = "2025-06-02T15:12:03.405Z" }, + { url = "https://files.pythonhosted.org/packages/a9/99/3ae339466c9183ea5b8ae87b34c0b897eda475d2aec2307cae60e5cd4f29/uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686", size = 11488 }, ] [[package]] name = "urllib3" version = "2.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795 }, ] [[package]] @@ -2205,9 +2293,9 @@ dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605, upload-time = "2025-10-18T13:46:44.63Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109, upload-time = "2025-10-18T13:46:42.958Z" }, + { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109 }, ] [package.optional-dependencies] @@ -2225,26 +2313,26 @@ standard = [ name = "uvloop" version = "0.22.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, - { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, - { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, - { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, - { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, - { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, - { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, - { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, - { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, - { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, - { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, - { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, - { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, - { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, - { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, - { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611 }, + { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811 }, + { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562 }, + { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890 }, + { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472 }, + { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051 }, + { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067 }, + { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423 }, + { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437 }, + { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101 }, + { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158 }, + { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360 }, + { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790 }, + { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783 }, + { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548 }, + { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065 }, + { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384 }, + { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730 }, ] [[package]] @@ -2254,74 +2342,74 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, - { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, - { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, - { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, - { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, - { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, - { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, - { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, - { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, - { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, - { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, - { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, - { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, - { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, - { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, - { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, - { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, - { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, - { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, - { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, - { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, - { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, - { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, - { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, - { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321 }, + { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783 }, + { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279 }, + { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405 }, + { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976 }, + { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506 }, + { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936 }, + { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147 }, + { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007 }, + { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280 }, + { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056 }, + { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162 }, + { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909 }, + { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389 }, + { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964 }, + { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114 }, + { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264 }, + { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877 }, + { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176 }, + { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577 }, + { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425 }, + { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826 }, + { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208 }, + { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315 }, + { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869 }, + { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919 }, + { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845 }, + { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027 }, + { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615 }, + { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836 }, + { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099 }, + { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626 }, + { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519 }, + { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078 }, + { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664 }, + { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154 }, + { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820 }, + { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510 }, + { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408 }, + { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968 }, + { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096 }, + { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040 }, + { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847 }, + { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072 }, + { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104 }, + { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112 }, ] [[package]] name = "websockets" version = "15.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, - { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, - { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, - { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, - { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, - { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, - { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440 }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098 }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329 }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111 }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054 }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496 }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829 }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217 }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195 }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393 }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837 }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 }, ] [[package]] @@ -2331,7 +2419,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6a/c7/96d10183c3470f1836846f7b9527d6cb0b6c2226ebca40f36fa29f23de60/wtforms-3.1.2.tar.gz", hash = "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9", size = 134705, upload-time = "2024-01-06T07:52:41.075Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/c7/96d10183c3470f1836846f7b9527d6cb0b6c2226ebca40f36fa29f23de60/wtforms-3.1.2.tar.gz", hash = "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9", size = 134705 } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/19/c3232f35e24dccfad372e9f341c4f3a1166ae7c66e4e1351a9467c921cc1/wtforms-3.1.2-py3-none-any.whl", hash = "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07", size = 145961, upload-time = "2024-01-06T07:52:43.023Z" }, + { url = "https://files.pythonhosted.org/packages/18/19/c3232f35e24dccfad372e9f341c4f3a1166ae7c66e4e1351a9467c921cc1/wtforms-3.1.2-py3-none-any.whl", hash = "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07", size = 145961 }, ] From 623961dfd2744cd7c131ef56f69cc09063a0a7e9 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Fri, 7 Nov 2025 16:55:18 +0000 Subject: [PATCH 31/79] fix(backend): Use try except else pattern --- backend/app/core/redis.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/app/core/redis.py b/backend/app/core/redis.py index 31e5302..ea99feb 100644 --- a/backend/app/core/redis.py +++ b/backend/app/core/redis.py @@ -35,11 +35,14 @@ async def init_redis() -> Redis | None: # Verify connection on startup await redis_client.pubsub().ping() logger.info("Redis client initialized and connected: %s:%s", settings.redis_host, settings.redis_port) - return redis_client except (TimeoutError, RedisError, OSError, ConnectionError) as e: - logger.warning("Failed to connect to Redis during initialization: %s. Application will continue without Redis.", e) + logger.warning( + "Failed to connect to Redis during initialization: %s. Application will continue without Redis.", e + ) return None + else: + return redis_client async def close_redis(redis_client: Redis) -> None: From dc94217c46422a5d36f23ac90ccc114e0aec9c89 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Fri, 7 Nov 2025 18:53:20 +0100 Subject: [PATCH 32/79] fix(docker): Persist cache data in prod --- compose.prod.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compose.prod.yml b/compose.prod.yml index 82908a0..e73373c 100644 --- a/compose.prod.yml +++ b/compose.prod.yml @@ -63,3 +63,6 @@ services: docs: # Disable live reload for production command: ["serve", "--dev-addr=0.0.0.0:8000", "--no-livereload"] + +volumes: + cache_data: From 009798234165e116c8c81b5104bf6150b6ef3d18 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Fri, 7 Nov 2025 23:02:34 +0100 Subject: [PATCH 33/79] fix(backend): Move Annotated validation from database models to Create and Udpate schemas --- .../app/api/common/models/custom_fields.py | 9 --------- .../app/api/common/schemas/custom_fields.py | 13 +++++++++++++ backend/app/api/file_storage/models/models.py | 14 +++++++++----- backend/app/api/file_storage/schemas.py | 10 ++++++++-- backend/app/api/newsletter/models.py | 8 ++++---- backend/app/api/newsletter/schemas.py | 6 +++++- backend/app/api/plugins/rpi_cam/models.py | 19 ++++++++++++------- backend/app/api/plugins/rpi_cam/schemas.py | 6 ++++-- 8 files changed, 55 insertions(+), 30 deletions(-) delete mode 100644 backend/app/api/common/models/custom_fields.py create mode 100644 backend/app/api/common/schemas/custom_fields.py diff --git a/backend/app/api/common/models/custom_fields.py b/backend/app/api/common/models/custom_fields.py deleted file mode 100644 index 343940e..0000000 --- a/backend/app/api/common/models/custom_fields.py +++ /dev/null @@ -1,9 +0,0 @@ -"""Custom Pydantic fields for database models.""" - -from typing import Annotated - -from pydantic import AnyUrl, HttpUrl, PlainSerializer - -# HTTP URL that is stored as string in the database. -HttpUrlInDB = Annotated[HttpUrl, PlainSerializer(lambda x: str(x), return_type=str)] -AnyUrlInDB = Annotated[AnyUrl, PlainSerializer(lambda x: str(x), return_type=str)] diff --git a/backend/app/api/common/schemas/custom_fields.py b/backend/app/api/common/schemas/custom_fields.py new file mode 100644 index 0000000..c50df7a --- /dev/null +++ b/backend/app/api/common/schemas/custom_fields.py @@ -0,0 +1,13 @@ +"""Shared fields for DTO schemas.""" + +from typing import Annotated + +from pydantic import AnyUrl, HttpUrl, PlainSerializer, StringConstraints + +# HTTP URL that is stored as string in the database. +type HttpUrlToDB = Annotated[ + HttpUrl, PlainSerializer(lambda x: str(x), return_type=str), StringConstraints(max_length=250) +] +type AnyUrlToDB = Annotated[ + AnyUrl, PlainSerializer(lambda x: str(x), return_type=str), StringConstraints(max_length=250) +] diff --git a/backend/app/api/file_storage/models/models.py b/backend/app/api/file_storage/models/models.py index ae78b78..d69fa48 100644 --- a/backend/app/api/file_storage/models/models.py +++ b/backend/app/api/file_storage/models/models.py @@ -10,11 +10,10 @@ from markupsafe import Markup from pydantic import UUID4, ConfigDict from sqlalchemy.dialects.postgresql import JSONB -from sqlmodel import AutoString, Column, Field, Relationship +from sqlmodel import Column, Field, Relationship from sqlmodel import Enum as SAEnum from app.api.common.models.base import APIModelName, CustomBase, SingleParentMixin, TimeStampMixinBare -from app.api.common.models.custom_fields import AnyUrlInDB from app.api.data_collection.models import Product from app.api.file_storage.exceptions import FastAPIStorageFileNotFoundError from app.api.file_storage.models.custom_types import FileType, ImageType @@ -49,7 +48,9 @@ class FileBase(CustomBase): class File(FileBase, TimeStampMixinBare, SingleParentMixin[FileParentType], table=True): """Database model for generic files stored in the local file system, using FastAPI-Storages.""" - id: UUID4 = Field(default_factory=uuid.uuid4, primary_key=True) + # HACK: Redefine id to allow None in the backend which is required by the > 2.12 pydantic/sqlmodel combo + id: UUID4 | None = Field(default_factory=uuid.uuid4, primary_key=True, nullable=False) + filename: str = Field(description="Original file name of the file. Automatically generated.") # TODO: Add custom file paths based on parent object (Product, year, etc.) @@ -111,7 +112,10 @@ class ImageBase(CustomBase): class Image(ImageBase, TimeStampMixinBare, SingleParentMixin, table=True): """Database model for images stored in the local file system, using FastAPI-Storages.""" - id: UUID4 = Field(default_factory=uuid.uuid4, primary_key=True) + # HACK: Redefine id to allow None in the backend which is required by the > 2.12 pydantic/sqlmodel combo + # TODO: To avoid this hack, for all database models, create a InDB child class that has non-optional id field + id: UUID4 | None = Field(default_factory=uuid.uuid4, primary_key=True, nullable=False) + filename: str = Field(description="Original file name of the image. Automatically generated.", nullable=False) file: ImageType = Field( sa_column=Column(ImageType, nullable=False), @@ -153,7 +157,7 @@ def image_preview(self, size: int = 100) -> str: class VideoBase(CustomBase): """Base model for videos stored online.""" - url: AnyUrlInDB = Field(description="URL linking to the video", sa_type=AutoString, nullable=False) + url: str = Field(description="URL linking to the video", nullable=False) title: str | None = Field(default=None, max_length=100, description="Title of the video") description: str | None = Field(default=None, max_length=500, description="Description of the video") video_metadata: dict[str, Any] | None = Field( diff --git a/backend/app/api/file_storage/schemas.py b/backend/app/api/file_storage/schemas.py index ab4bf4b..ed737a0 100644 --- a/backend/app/api/file_storage/schemas.py +++ b/backend/app/api/file_storage/schemas.py @@ -3,10 +3,11 @@ from typing import Annotated, Any from fastapi import UploadFile -from pydantic import AfterValidator, Field, HttpUrl, Json, PositiveInt +from pydantic import AfterValidator, Field, Json, PositiveInt from app.api.common.models.custom_types import IDT from app.api.common.schemas.base import BaseCreateSchema, BaseReadSchemaWithTimeStamp, BaseUpdateSchema +from app.api.common.schemas.custom_fields import AnyUrlToDB from app.api.file_storage.models.models import FileBase, FileParentType, ImageBase, ImageParentType, VideoBase ### Constants ### @@ -171,10 +172,15 @@ class ImageUpdate(BaseUpdateSchema, ImageBase): class VideoCreateWithinProduct(BaseCreateSchema, VideoBase): """Schema for creating a video.""" + # Override url field to add validation + url: AnyUrlToDB + class VideoCreate(BaseCreateSchema, VideoBase): """Schema for creating a video.""" + # Override url field to add validation + url: AnyUrlToDB product_id: PositiveInt @@ -191,7 +197,7 @@ class VideoRead(BaseReadSchemaWithTimeStamp, VideoBase): class VideoUpdate(BaseUpdateSchema): """Schema for updating a video.""" - url: HttpUrl | None = Field(default=None, max_length=250, description="HTTP(S) URL linking to the video") + url: AnyUrlToDB | None = Field(default=None, description="URL linking to the video") title: str | None = Field(default=None, max_length=100, description="Title of the video") description: str | None = Field(default=None, max_length=500, description="Description of the video") video_metadata: dict[str, Any] | None = Field(default=None, description="Video metadata as a JSON dict") diff --git a/backend/app/api/newsletter/models.py b/backend/app/api/newsletter/models.py index 1bbdc23..5c16113 100644 --- a/backend/app/api/newsletter/models.py +++ b/backend/app/api/newsletter/models.py @@ -1,9 +1,8 @@ """Database models related to newsletter subscribers.""" import uuid -from typing import Annotated -from pydantic import UUID4, EmailStr, StringConstraints +from pydantic import UUID4, EmailStr from sqlmodel import Field from app.api.common.models.base import CustomBase, TimeStampMixinBare @@ -12,11 +11,12 @@ class NewsletterSubscriberBase(CustomBase): """Base schema for newsletter subscribers.""" - email: Annotated[EmailStr, StringConstraints(strip_whitespace=True)] = Field(index=True, unique=True) + email: EmailStr = Field(index=True, unique=True) class NewsletterSubscriber(NewsletterSubscriberBase, TimeStampMixinBare, table=True): """Database model for newsletter subscribers.""" - id: UUID4 = Field(default_factory=uuid.uuid4, primary_key=True, nullable=False) + # HACK: Redefine id to allow None in the backend which is required by the > 2.12 pydantic/sqlmodel combo + id: UUID4 | None = Field(default_factory=uuid.uuid4, primary_key=True, nullable=False) is_confirmed: bool = Field(default=False) diff --git a/backend/app/api/newsletter/schemas.py b/backend/app/api/newsletter/schemas.py index cacc5bd..0e237a0 100644 --- a/backend/app/api/newsletter/schemas.py +++ b/backend/app/api/newsletter/schemas.py @@ -1,6 +1,8 @@ """DTO schemas for newsletter subscribers.""" -from pydantic import Field +from typing import Annotated + +from pydantic import EmailStr, Field, StringConstraints from app.api.common.schemas.base import BaseCreateSchema, BaseReadSchemaWithTimeStamp from app.api.newsletter.models import NewsletterSubscriberBase @@ -9,6 +11,8 @@ class NewsletterSubscriberCreate(BaseCreateSchema, NewsletterSubscriberBase): """Create schema for newsletter subscribers.""" + email: Annotated[EmailStr, StringConstraints(strip_whitespace=True)] = Field() + class NewsletterSubscriberRead(BaseReadSchemaWithTimeStamp, NewsletterSubscriberBase): """Read schema for newsletter subscribers.""" diff --git a/backend/app/api/plugins/rpi_cam/models.py b/backend/app/api/plugins/rpi_cam/models.py index 974fab6..fae5de8 100644 --- a/backend/app/api/plugins/rpi_cam/models.py +++ b/backend/app/api/plugins/rpi_cam/models.py @@ -14,7 +14,6 @@ from sqlmodel import AutoString, Field, Relationship from app.api.common.models.base import CustomBase, TimeStampMixinBare -from app.api.common.models.custom_fields import HttpUrlInDB from app.api.plugins.rpi_cam.config import settings from app.api.plugins.rpi_cam.utils.encryption import decrypt_dict, decrypt_str, encrypt_dict @@ -68,23 +67,29 @@ class CameraBase(CustomBase): # NOTE: Local addresses only work when they are on the local network of this API # TODO: Add support for server communication to local network cameras for users via websocket or similar - # NOTE: Database models will have url as string type. This is likely because of how sa_type=Autostring works - # This means HttpUrl methods are not available in database model instances. - # TODO: Only validate the URL format in Pydantic schemas and store as plain string in the database model. - url: HttpUrlInDB = Field(description="HTTP(S) URL where the camera API is hosted", sa_type=AutoString) + # NOTE: URL validation is done in the Pydantic schemas (CameraCreate/CameraUpdate). + # The database stores it as a plain string. + url: str = Field(description="HTTP(S) URL where the camera API is hosted", sa_type=AutoString) class Camera(CameraBase, TimeStampMixinBare, table=True): """Database model for Camera.""" - id: UUID4 = Field(default_factory=uuid.uuid4, primary_key=True) + # HACK: Redefine id to allow None in the backend which is required by the > 2.12 pydantic/sqlmodel combo + id: UUID4 | None = Field(default_factory=uuid.uuid4, primary_key=True, nullable=False) + encrypted_api_key: str = Field(nullable=False) # TODO: Consider merging encrypted_auth_headers and encrypted_api_key into a single encrypted_credentials field encrypted_auth_headers: str | None = Field(default=None) # Many-to-one relationship with User owner_id: UUID4 = Field(foreign_key="user.id") - owner: "User" = Relationship() # One-way relationship to maintain plugin isolation + owner: "User" = Relationship( # One-way relationship to maintain plugin isolation + sa_relationship_kwargs={ + "primaryjoin": "Camera.owner_id == User.id", + "foreign_keys": "[Camera.owner_id]", + } + ) @computed_field @cached_property diff --git a/backend/app/api/plugins/rpi_cam/schemas.py b/backend/app/api/plugins/rpi_cam/schemas.py index 18de973..1240978 100644 --- a/backend/app/api/plugins/rpi_cam/schemas.py +++ b/backend/app/api/plugins/rpi_cam/schemas.py @@ -9,7 +9,6 @@ AfterValidator, BaseModel, Field, - HttpUrl, PlainSerializer, SecretStr, ) @@ -20,6 +19,7 @@ BaseReadSchemaWithTimeStamp, BaseUpdateSchema, ) +from app.api.common.schemas.custom_fields import HttpUrlToDB from app.api.plugins.rpi_cam.config import settings from app.api.plugins.rpi_cam.models import Camera, CameraBase, CameraStatus from app.api.plugins.rpi_cam.utils.encryption import decrypt_str @@ -107,6 +107,8 @@ def validate_auth_headers_size(headers: list[HeaderCreate] | None) -> list[Heade class CameraCreate(BaseCreateSchema, CameraBase): """Schema for creating a camera.""" + # Override url field to add validation + url: HttpUrlToDB = Field(description="HTTP(S) URL where the camera API is hosted") auth_headers: OptionalAuthHeaderCreateList @@ -156,7 +158,7 @@ class CameraUpdate(BaseUpdateSchema): name: str | None = Field(default=None, min_length=2, max_length=100) description: str | None = Field(default=None, max_length=500) - url: HttpUrl | None = Field(default=None, description="HTTP(S) URL where the camera API is hosted") + url: HttpUrlToDB | None = Field(default=None, description="HTTP(S) URL where the camera API is hosted") auth_headers: OptionalAuthHeaderCreateList # TODO: Make it only possible to change ownership to existing users within the same organization From 5194bd3f272fa2993373dc6da13ad3431f8e8aec Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Sat, 8 Nov 2025 11:01:51 +0100 Subject: [PATCH 34/79] feature(backend): Fix Pydantic 2.12 issues and move to python 3.14 --- backend/.python-version | 2 +- backend/Dockerfile | 4 +- backend/Dockerfile.dev | 2 +- backend/Dockerfile.migrations | 4 +- backend/app/api/auth/models.py | 70 +- backend/app/api/common/models/base.py | 2 +- backend/app/api/data_collection/models.py | 8 +- backend/pyproject.toml | 9 +- backend/uv.lock | 1981 +++++++++++---------- codemeta.json | 2 +- 10 files changed, 1082 insertions(+), 1002 deletions(-) diff --git a/backend/.python-version b/backend/.python-version index 24ee5b1..6324d40 100644 --- a/backend/.python-version +++ b/backend/.python-version @@ -1 +1 @@ -3.13 +3.14 diff --git a/backend/Dockerfile b/backend/Dockerfile index 0d3fee1..17b19a7 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,5 +1,5 @@ # --- Builder stage --- -FROM ghcr.io/astral-sh/uv:0.9-python3.13-trixie-slim@sha256:87db60325200a4fa5e9259fe43ff14c90c429adee952a8efe3f21b278409d09a AS builder +FROM ghcr.io/astral-sh/uv:0.9-python3.14-trixie-slim AS builder # Install git for custom dependencies (fastapi-users-db-sqlmodel) RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ @@ -34,7 +34,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --locked --no-editable --no-default-groups --group=api # --- Final runtime stage --- -FROM python:3.13-slim +FROM python:3.14-slim # Build arguments ARG WORKDIR=/opt/relab/backend diff --git a/backend/Dockerfile.dev b/backend/Dockerfile.dev index dafcde7..36cea79 100644 --- a/backend/Dockerfile.dev +++ b/backend/Dockerfile.dev @@ -1,6 +1,6 @@ # Development Dockerfile for FastAPI Backend # Note: This requires mounting the source code as a volume in docker-compose.override.yml -FROM ghcr.io/astral-sh/uv:0.9-python3.13-trixie-slim@sha256:87db60325200a4fa5e9259fe43ff14c90c429adee952a8efe3f21b278409d09a +FROM ghcr.io/astral-sh/uv:0.9-python3.14-trixie-slim # Build arguments ARG WORKDIR=/opt/relab/backend diff --git a/backend/Dockerfile.migrations b/backend/Dockerfile.migrations index 92cecd6..73a3932 100644 --- a/backend/Dockerfile.migrations +++ b/backend/Dockerfile.migrations @@ -1,5 +1,5 @@ # --- Builder stage --- -FROM ghcr.io/astral-sh/uv:0.9-python3.13-trixie-slim@sha256:87db60325200a4fa5e9259fe43ff14c90c429adee952a8efe3f21b278409d09a AS builder +FROM ghcr.io/astral-sh/uv:0.9-python3.14-trixie-slim AS builder WORKDIR /opt/relab/backend_migrations @@ -33,7 +33,7 @@ COPY scripts/ scripts/ COPY app/ app/ # --- Final runtime stage --- -FROM python:3.13-slim +FROM python:3.14-slim # Build arguments ARG WORKDIR=/opt/relab/backend_migrations diff --git a/backend/app/api/auth/models.py b/backend/app/api/auth/models.py index 5b507f0..206f81a 100644 --- a/backend/app/api/auth/models.py +++ b/backend/app/api/auth/models.py @@ -36,26 +36,58 @@ class UserBase(BaseModel): model_config = ConfigDict(use_enum_values=True) # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 -class User(UserBase, CustomBaseBare, TimeStampMixinBare, SQLModelBaseUserDB, table=True): +class User(SQLModelBaseUserDB, CustomBaseBare, UserBase, TimeStampMixinBare, table=True): """Database model for platform users.""" + # HACK: Redefine id to allow None in the backend which is required by the > 2.12 pydantic/sqlmodel combo (see https://github.com/fastapi/sqlmodel/issues/1623) + id: UUID4 | None = Field(default_factory=uuid.uuid4, primary_key=True, nullable=False) + # One-to-many relationship with OAuthAccount oauth_accounts: list["OAuthAccount"] = Relationship( back_populates="user", - sa_relationship_kwargs={"lazy": "joined"}, # Required because of FastAPI-Users OAuth implementation + sa_relationship_kwargs={ + "lazy": "joined", # Required because of FastAPI-Users OAuth implementation + "primaryjoin": "User.id == OAuthAccount.user_id", # HACK: Explicitly define join condition because of + "foreign_keys": "[OAuthAccount.user_id]", # pydantic / sqlmodel issues (see https://github.com/fastapi/sqlmodel/issues/1623) + }, # TODO: Check if this is fixed in future versions of pydantic/sqlmodel and we can use automatic + # relationship detection again + ) + products: list["Product"] = Relationship( + back_populates="owner", + sa_relationship_kwargs={ + "primaryjoin": "User.id == Product.owner_id", # HACK: Explicitly define join condition because of + "foreign_keys": "[Product.owner_id]", # pydantic / sqlmodel issues + }, ) - products: list["Product"] = Relationship(back_populates="owner") # Many-to-one relationship with Organization organization_id: UUID4 | None = Field( default=None, - sa_column=Column(ForeignKey("organization.id", use_alter=True, name="fk_user_organization"), nullable=True), + sa_column=Column( + ForeignKey("organization.id", use_alter=True, name="fk_user_organization"), + nullable=True, + ), ) organization: Optional["Organization"] = Relationship( - back_populates="members", sa_relationship_kwargs={"lazy": "selectin", "foreign_keys": "[User.organization_id]"} + back_populates="members", + sa_relationship_kwargs={ + "lazy": "selectin", + "primaryjoin": "User.organization_id == Organization.id", # HACK: Explicitly define join condition because of + "foreign_keys": "[User.organization_id]", # pydantic / sqlmodel issues + }, ) organization_role: OrganizationRole | None = Field(default=None, sa_column=Column(SAEnum(OrganizationRole))) + # One-to-one relationship with owned Organization + owned_organization: Optional["Organization"] = Relationship( + back_populates="owner", + sa_relationship_kwargs={ + "uselist": False, + "primaryjoin": "User.id == Organization.owner_id", # HACK: Explicitly define join condition because of + "foreign_keys": "[Organization.owner_id]", # pydantic / sqlmodel issues + }, + ) + @cached_property def is_organization_owner(self) -> bool: return self.organization_role == OrganizationRole.OWNER @@ -68,8 +100,20 @@ def __str__(self) -> str: class OAuthAccount(SQLModelBaseOAuthAccount, CustomBaseBare, TimeStampMixinBare, table=True): """Database model for OAuth accounts. Note that the main implementation is in the base class.""" + # HACK: Redefine id to allow None in the backend which is required by the > 2.12 pydantic/sqlmodel combo + id: UUID4 | None = Field(default_factory=uuid.uuid4, primary_key=True, nullable=False) + + # HACK: Redefine user_id to ensure ForeignKey is preserved despite mixin interference + user_id: UUID4 = Field(foreign_key="user.id", nullable=False) + # Many-to-one relationship with User - user: User = Relationship(back_populates="oauth_accounts") + user: User = Relationship( + back_populates="oauth_accounts", + sa_relationship_kwargs={ # HACK: Explicitly define join condition because of pydantic / sqlmodel issues + "primaryjoin": "OAuthAccount.user_id == User.id", # (see https://github.com/fastapi/sqlmodel/issues/1623) + "foreign_keys": "[OAuthAccount.user_id]", + }, + ) ### Organization Model ### @@ -84,15 +128,21 @@ class OrganizationBase(CustomBase): class Organization(OrganizationBase, TimeStampMixinBare, table=True): """Database model for organizations.""" - id: UUID4 = Field(default_factory=uuid.uuid4, primary_key=True, nullable=False) + # HACK: Redefine id to allow None in the backend which is required by the > 2.12 pydantic/sqlmodel combo + id: UUID4 | None = Field(default_factory=uuid.uuid4, primary_key=True, nullable=False) # One-to-one relationship with owner User + # Use sa_column with explicit ForeignKey to preserve constraint through mixin inheritance owner_id: UUID4 = Field( - sa_column=Column(ForeignKey("user.id", use_alter=True, name="fk_organization_owner"), nullable=False), + sa_column=Column(ForeignKey("user.id", use_alter=True, name="fk_organization_owner"), nullable=False) ) owner: User = Relationship( - back_populates="organization", - sa_relationship_kwargs={"primaryjoin": "Organization.owner_id == User.id", "foreign_keys": "[User.id]"}, + back_populates="owned_organization", + sa_relationship_kwargs={ + "uselist": False, + "primaryjoin": "Organization.owner_id == User.id", # HACK: Explicitly define join condition because of + "foreign_keys": "[Organization.owner_id]", # pydantic / sqlmodel issues + }, ) # One-to-many relationship with member Users diff --git a/backend/app/api/common/models/base.py b/backend/app/api/common/models/base.py index 1a43b9c..54e3b41 100644 --- a/backend/app/api/common/models/base.py +++ b/backend/app/api/common/models/base.py @@ -4,7 +4,7 @@ from datetime import datetime from enum import Enum from functools import cached_property -from typing import Any, ClassVar, Generic, Self, TypeVar +from typing import Any, ClassVar, Self, TypeVar from pydantic import BaseModel, ConfigDict, computed_field, model_validator from sqlalchemy import TIMESTAMP, func diff --git a/backend/app/api/data_collection/models.py b/backend/app/api/data_collection/models.py index 9afe6fa..4cb5388 100644 --- a/backend/app/api/data_collection/models.py +++ b/backend/app/api/data_collection/models.py @@ -126,7 +126,13 @@ class Product(ProductBase, TimeStampMixinBare, table=True): # One-to-many relationships owner_id: UUID4 = Field(foreign_key="user.id") owner: "User" = Relationship( - back_populates="products", sa_relationship_kwargs={"uselist": False, "lazy": "selectin"} + back_populates="products", + sa_relationship_kwargs={ + "uselist": False, + "lazy": "selectin", + "primaryjoin": "Product.owner_id == User.id", # HACK: Explicitly define join condition because of + "foreign_keys": "[Product.owner_id]", # pydantic / sqlmodel issues (see https://github.com/fastapi/sqlmodel/issues/1623) + }, ) product_type_id: int | None = Field(default=None, foreign_key="producttype.id") diff --git a/backend/pyproject.toml b/backend/pyproject.toml index cd97213..860929e 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -41,8 +41,7 @@ "mjml>=0.11.1", "pillow >=11.2.1", "psycopg[binary] >=3.2.9", - # TODO: Upgrade to python 3.14 and pydantic 2.12 when SQLModel fixes compatibility issues (see https://github.com/fastapi/sqlmodel/issues/1606) - "pydantic >=2.11,<2.12", + "pydantic >=2.12", "pydantic-extra-types >=2.10.5", "pydantic-settings >=2.10.1", "python-dotenv >=1.1.1", @@ -50,7 +49,7 @@ "redis>=5.2.1", "relab-rpi-cam-models>=0.1.1", "sqlalchemy >=2.0.41", - "sqlmodel >=0.0.24", + "sqlmodel >=0.0.27", "tldextract>=5.3.0", ] requires-python = ">= 3.13" @@ -249,7 +248,7 @@ default-groups = ["api", "dev", "migrations", "tests"] [tool.uv.sources] - # HACK: Fetch FastAPI-Mail from custom fork on GitHub to allow passing existing Redis client and fix compatibility issues with Pydantic > 2.12 and SQLModel (see https://github.com/fastapi/sqlmodel/issues/1623) - fastapi-mail = { git = "https://github.com/simonvanlierde/fastapi-mail", rev = "f32147ec1a450ed22262913c5ac7ec3b67dd0117" } + # HACK: Fetch FastAPI-Mail from custom fork on GitHub to allow passing existing Redis client + fastapi-mail = { git = "https://github.com/simonvanlierde/fastapi-mail", rev = "6c6f04a7afaf3cdced82764009a2f1f2a3c3ee6c" } # Fetch FastAPI-Users-DB-SQLModel from custom fork on GitHub for Pydantic V2 support fastapi-users-db-sqlmodel = { git = "https://github.com/simonvanlierde/fastapi-users-db-sqlmodel", rev = "7e9c4830e53ee20c38e3de80066cb19d7c3efc43" } diff --git a/backend/uv.lock b/backend/uv.lock index cfb072d..c60bc1c 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 1 +revision = 3 requires-python = ">=3.13" resolution-markers = [ "python_full_version >= '3.14'", @@ -10,9 +10,9 @@ resolution-markers = [ name = "aiosmtplib" version = "4.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/e1/cc58e0be242f0b410707e001ed22c689435964fcaab42108887426e44fff/aiosmtplib-4.0.2.tar.gz", hash = "sha256:f0b4933e7270a8be2b588761e5b12b7334c11890ee91987c2fb057e72f566da6", size = 61052 } +sdist = { url = "https://files.pythonhosted.org/packages/0f/e1/cc58e0be242f0b410707e001ed22c689435964fcaab42108887426e44fff/aiosmtplib-4.0.2.tar.gz", hash = "sha256:f0b4933e7270a8be2b588761e5b12b7334c11890ee91987c2fb057e72f566da6", size = 61052, upload-time = "2025-08-25T02:39:07.249Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/2f/db9414bbeacee48ab0c7421a0319b361b7c15b5c3feebcd38684f5d5f849/aiosmtplib-4.0.2-py3-none-any.whl", hash = "sha256:72491f96e6de035c28d29870186782eccb2f651db9c5f8a32c9db689327f5742", size = 27048 }, + { url = "https://files.pythonhosted.org/packages/f1/2f/db9414bbeacee48ab0c7421a0319b361b7c15b5c3feebcd38684f5d5f849/aiosmtplib-4.0.2-py3-none-any.whl", hash = "sha256:72491f96e6de035c28d29870186782eccb2f651db9c5f8a32c9db689327f5742", size = 27048, upload-time = "2025-08-25T02:39:06.089Z" }, ] [[package]] @@ -24,9 +24,9 @@ dependencies = [ { name = "sqlalchemy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6e/b6/2a81d7724c0c124edc5ec7a167e85858b6fd31b9611c6fb8ecf617b7e2d3/alembic-1.17.1.tar.gz", hash = "sha256:8a289f6778262df31571d29cca4c7fbacd2f0f582ea0816f4c399b6da7528486", size = 1981285 } +sdist = { url = "https://files.pythonhosted.org/packages/6e/b6/2a81d7724c0c124edc5ec7a167e85858b6fd31b9611c6fb8ecf617b7e2d3/alembic-1.17.1.tar.gz", hash = "sha256:8a289f6778262df31571d29cca4c7fbacd2f0f582ea0816f4c399b6da7528486", size = 1981285, upload-time = "2025-10-29T00:23:16.667Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/32/7df1d81ec2e50fb661944a35183d87e62d3f6c6d9f8aff64a4f245226d55/alembic-1.17.1-py3-none-any.whl", hash = "sha256:cbc2386e60f89608bb63f30d2d6cc66c7aaed1fe105bd862828600e5ad167023", size = 247848 }, + { url = "https://files.pythonhosted.org/packages/a5/32/7df1d81ec2e50fb661944a35183d87e62d3f6c6d9f8aff64a4f245226d55/alembic-1.17.1-py3-none-any.whl", hash = "sha256:cbc2386e60f89608bb63f30d2d6cc66c7aaed1fe105bd862828600e5ad167023", size = 247848, upload-time = "2025-10-29T00:23:18.79Z" }, ] [[package]] @@ -37,9 +37,9 @@ dependencies = [ { name = "alembic" }, { name = "click" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d2/bb/6e5eb52a6695690f91b7f67027f7a498ecb0e307f4f2e7d0ae0f854059f5/alembic-autogen-check-1.1.1.tar.gz", hash = "sha256:cdda293a71b2413e854b07641c6f8291dffca0c5c6d0531b7b457629a30ca9cf", size = 2660 } +sdist = { url = "https://files.pythonhosted.org/packages/d2/bb/6e5eb52a6695690f91b7f67027f7a498ecb0e307f4f2e7d0ae0f854059f5/alembic-autogen-check-1.1.1.tar.gz", hash = "sha256:cdda293a71b2413e854b07641c6f8291dffca0c5c6d0531b7b457629a30ca9cf", size = 2660, upload-time = "2019-05-10T21:45:02.015Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/10/57410287f55b37aff354aa078d66f4a759b753ecb7d5aa1225174e1fd6ee/alembic_autogen_check-1.1.1-py3-none-any.whl", hash = "sha256:331c90b99cc2d1c40e69205dfd5e44b5d9c8f111e4b96244f79b303398740659", size = 3968 }, + { url = "https://files.pythonhosted.org/packages/24/10/57410287f55b37aff354aa078d66f4a759b753ecb7d5aa1225174e1fd6ee/alembic_autogen_check-1.1.1-py3-none-any.whl", hash = "sha256:331c90b99cc2d1c40e69205dfd5e44b5d9c8f111e4b96244f79b303398740659", size = 3968, upload-time = "2019-05-10T21:45:01.039Z" }, ] [[package]] @@ -50,27 +50,27 @@ dependencies = [ { name = "alembic" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/04/e465cb5c051fb056b7fadda7667b3e1fb4d32d7f19533e3bbff071c73788/alembic_postgresql_enum-1.8.0.tar.gz", hash = "sha256:132cd5fdc4a2a0b6498f3d89ea1c7b2a5ddc3281ddd84edae7259ec4c0a215a0", size = 15858 } +sdist = { url = "https://files.pythonhosted.org/packages/58/04/e465cb5c051fb056b7fadda7667b3e1fb4d32d7f19533e3bbff071c73788/alembic_postgresql_enum-1.8.0.tar.gz", hash = "sha256:132cd5fdc4a2a0b6498f3d89ea1c7b2a5ddc3281ddd84edae7259ec4c0a215a0", size = 15858, upload-time = "2025-07-20T12:25:50.626Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/80/4e6e841f9a0403b520b8f28650c2cdf5905e25bd4ff403b43daec580fed3/alembic_postgresql_enum-1.8.0-py3-none-any.whl", hash = "sha256:0e62833f8d1aca2c58fa09cae1d4a52472fb32d2dde32b68c84515fffcf401d5", size = 23697 }, + { url = "https://files.pythonhosted.org/packages/77/80/4e6e841f9a0403b520b8f28650c2cdf5905e25bd4ff403b43daec580fed3/alembic_postgresql_enum-1.8.0-py3-none-any.whl", hash = "sha256:0e62833f8d1aca2c58fa09cae1d4a52472fb32d2dde32b68c84515fffcf401d5", size = 23697, upload-time = "2025-07-20T12:25:49.048Z" }, ] [[package]] name = "annotated-doc" version = "0.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/a6/dc46877b911e40c00d395771ea710d5e77b6de7bacd5fdcd78d70cc5a48f/annotated_doc-0.0.3.tar.gz", hash = "sha256:e18370014c70187422c33e945053ff4c286f453a984eba84d0dbfa0c935adeda", size = 5535 } +sdist = { url = "https://files.pythonhosted.org/packages/d7/a6/dc46877b911e40c00d395771ea710d5e77b6de7bacd5fdcd78d70cc5a48f/annotated_doc-0.0.3.tar.gz", hash = "sha256:e18370014c70187422c33e945053ff4c286f453a984eba84d0dbfa0c935adeda", size = 5535, upload-time = "2025-10-24T14:57:10.718Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/02/b7/cf592cb5de5cb3bade3357f8d2cf42bf103bbe39f459824b4939fd212911/annotated_doc-0.0.3-py3-none-any.whl", hash = "sha256:348ec6664a76f1fd3be81f43dffbee4c7e8ce931ba71ec67cc7f4ade7fbbb580", size = 5488 }, + { url = "https://files.pythonhosted.org/packages/02/b7/cf592cb5de5cb3bade3357f8d2cf42bf103bbe39f459824b4939fd212911/annotated_doc-0.0.3-py3-none-any.whl", hash = "sha256:348ec6664a76f1fd3be81f43dffbee4c7e8ce931ba71ec67cc7f4ade7fbbb580", size = 5488, upload-time = "2025-10-24T14:57:09.462Z" }, ] [[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 } +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 }, + { 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]] @@ -81,9 +81,9 @@ dependencies = [ { name = "idna" }, { name = "sniffio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094 } +sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097 }, + { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, ] [[package]] @@ -93,9 +93,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argon2-cffi-bindings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/31/fa/57ec2c6d16ecd2ba0cf15f3c7d1c3c2e7b5fcb83555ff56d7ab10888ec8f/argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08", size = 42798 } +sdist = { url = "https://files.pythonhosted.org/packages/31/fa/57ec2c6d16ecd2ba0cf15f3c7d1c3c2e7b5fcb83555ff56d7ab10888ec8f/argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08", size = 42798, upload-time = "2023-08-15T14:13:12.711Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/6a/e8a041599e78b6b3752da48000b14c8d1e8a04ded09c88c714ba047f34f5/argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea", size = 15124 }, + { url = "https://files.pythonhosted.org/packages/a4/6a/e8a041599e78b6b3752da48000b14c8d1e8a04ded09c88c714ba047f34f5/argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea", size = 15124, upload-time = "2023-08-15T14:13:10.752Z" }, ] [[package]] @@ -105,28 +105,28 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/3c0a35f46e52108d4707c44b95cfe2afcafc50800b5450c197454569b776/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f", size = 54393 }, - { url = "https://files.pythonhosted.org/packages/9d/f4/98bbd6ee89febd4f212696f13c03ca302b8552e7dbf9c8efa11ea4a388c3/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b", size = 29328 }, - { url = "https://files.pythonhosted.org/packages/43/24/90a01c0ef12ac91a6be05969f29944643bc1e5e461155ae6559befa8f00b/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a", size = 31269 }, - { url = "https://files.pythonhosted.org/packages/d4/d3/942aa10782b2697eee7af5e12eeff5ebb325ccfb86dd8abda54174e377e4/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44", size = 86558 }, - { url = "https://files.pythonhosted.org/packages/0d/82/b484f702fec5536e71836fc2dbc8c5267b3f6e78d2d539b4eaa6f0db8bf8/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb", size = 92364 }, - { url = "https://files.pythonhosted.org/packages/c9/c1/a606ff83b3f1735f3759ad0f2cd9e038a0ad11a3de3b6c673aa41c24bb7b/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92", size = 85637 }, - { url = "https://files.pythonhosted.org/packages/44/b4/678503f12aceb0262f84fa201f6027ed77d71c5019ae03b399b97caa2f19/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85", size = 91934 }, - { url = "https://files.pythonhosted.org/packages/f0/c7/f36bd08ef9bd9f0a9cff9428406651f5937ce27b6c5b07b92d41f91ae541/argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f", size = 28158 }, - { url = "https://files.pythonhosted.org/packages/b3/80/0106a7448abb24a2c467bf7d527fe5413b7fdfa4ad6d6a96a43a62ef3988/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6", size = 32597 }, - { url = "https://files.pythonhosted.org/packages/05/b8/d663c9caea07e9180b2cb662772865230715cbd573ba3b5e81793d580316/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623", size = 28231 }, - { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121 }, - { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177 }, - { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090 }, - { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246 }, - { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126 }, - { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343 }, - { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777 }, - { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180 }, - { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715 }, - { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149 }, +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/3c0a35f46e52108d4707c44b95cfe2afcafc50800b5450c197454569b776/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f", size = 54393, upload-time = "2025-07-30T10:01:40.97Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f4/98bbd6ee89febd4f212696f13c03ca302b8552e7dbf9c8efa11ea4a388c3/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b", size = 29328, upload-time = "2025-07-30T10:01:41.916Z" }, + { url = "https://files.pythonhosted.org/packages/43/24/90a01c0ef12ac91a6be05969f29944643bc1e5e461155ae6559befa8f00b/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a", size = 31269, upload-time = "2025-07-30T10:01:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/d4/d3/942aa10782b2697eee7af5e12eeff5ebb325ccfb86dd8abda54174e377e4/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44", size = 86558, upload-time = "2025-07-30T10:01:43.943Z" }, + { url = "https://files.pythonhosted.org/packages/0d/82/b484f702fec5536e71836fc2dbc8c5267b3f6e78d2d539b4eaa6f0db8bf8/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb", size = 92364, upload-time = "2025-07-30T10:01:44.887Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c1/a606ff83b3f1735f3759ad0f2cd9e038a0ad11a3de3b6c673aa41c24bb7b/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92", size = 85637, upload-time = "2025-07-30T10:01:46.225Z" }, + { url = "https://files.pythonhosted.org/packages/44/b4/678503f12aceb0262f84fa201f6027ed77d71c5019ae03b399b97caa2f19/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85", size = 91934, upload-time = "2025-07-30T10:01:47.203Z" }, + { url = "https://files.pythonhosted.org/packages/f0/c7/f36bd08ef9bd9f0a9cff9428406651f5937ce27b6c5b07b92d41f91ae541/argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f", size = 28158, upload-time = "2025-07-30T10:01:48.341Z" }, + { url = "https://files.pythonhosted.org/packages/b3/80/0106a7448abb24a2c467bf7d527fe5413b7fdfa4ad6d6a96a43a62ef3988/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6", size = 32597, upload-time = "2025-07-30T10:01:49.112Z" }, + { url = "https://files.pythonhosted.org/packages/05/b8/d663c9caea07e9180b2cb662772865230715cbd573ba3b5e81793d580316/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623", size = 28231, upload-time = "2025-07-30T10:01:49.92Z" }, + { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" }, + { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" }, + { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" }, + { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246, upload-time = "2025-07-30T10:01:54.145Z" }, + { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126, upload-time = "2025-07-30T10:01:55.074Z" }, + { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343, upload-time = "2025-07-30T10:01:56.007Z" }, + { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777, upload-time = "2025-07-30T10:01:56.943Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180, upload-time = "2025-07-30T10:01:57.759Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715, upload-time = "2025-07-30T10:01:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" }, ] [[package]] @@ -136,75 +136,75 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/cf/17f8a6b6b97f77b5981fbce1266913e718daaa3467b46f60a785cbaadc29/asyncache-0.3.1.tar.gz", hash = "sha256:9a1e60a75668e794657489bdea6540ee7e3259c483517b934670db7600bf5035", size = 3797 } +sdist = { url = "https://files.pythonhosted.org/packages/49/cf/17f8a6b6b97f77b5981fbce1266913e718daaa3467b46f60a785cbaadc29/asyncache-0.3.1.tar.gz", hash = "sha256:9a1e60a75668e794657489bdea6540ee7e3259c483517b934670db7600bf5035", size = 3797, upload-time = "2022-11-15T10:06:47.476Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/94/51927deb4f40872361ec4f5534f68f7a9ce81c4ef20bf5cd765307f4c15d/asyncache-0.3.1-py3-none-any.whl", hash = "sha256:ef20a1024d265090dd1e0785c961cf98b9c32cc7d9478973dcf25ac1b80011f5", size = 3722 }, + { url = "https://files.pythonhosted.org/packages/2f/94/51927deb4f40872361ec4f5534f68f7a9ce81c4ef20bf5cd765307f4c15d/asyncache-0.3.1-py3-none-any.whl", hash = "sha256:ef20a1024d265090dd1e0785c961cf98b9c32cc7d9478973dcf25ac1b80011f5", size = 3722, upload-time = "2022-11-15T10:06:45.546Z" }, ] [[package]] name = "asyncpg" version = "0.30.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/4c/7c991e080e106d854809030d8584e15b2e996e26f16aee6d757e387bc17d/asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851", size = 957746 } +sdist = { url = "https://files.pythonhosted.org/packages/2f/4c/7c991e080e106d854809030d8584e15b2e996e26f16aee6d757e387bc17d/asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851", size = 957746, upload-time = "2024-10-20T00:30:41.127Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/22/e20602e1218dc07692acf70d5b902be820168d6282e69ef0d3cb920dc36f/asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70", size = 670373 }, - { url = "https://files.pythonhosted.org/packages/3d/b3/0cf269a9d647852a95c06eb00b815d0b95a4eb4b55aa2d6ba680971733b9/asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3", size = 634745 }, - { url = "https://files.pythonhosted.org/packages/8e/6d/a4f31bf358ce8491d2a31bfe0d7bcf25269e80481e49de4d8616c4295a34/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33", size = 3512103 }, - { url = "https://files.pythonhosted.org/packages/96/19/139227a6e67f407b9c386cb594d9628c6c78c9024f26df87c912fabd4368/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4", size = 3592471 }, - { url = "https://files.pythonhosted.org/packages/67/e4/ab3ca38f628f53f0fd28d3ff20edff1c975dd1cb22482e0061916b4b9a74/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4", size = 3496253 }, - { url = "https://files.pythonhosted.org/packages/ef/5f/0bf65511d4eeac3a1f41c54034a492515a707c6edbc642174ae79034d3ba/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba", size = 3662720 }, - { url = "https://files.pythonhosted.org/packages/e7/31/1513d5a6412b98052c3ed9158d783b1e09d0910f51fbe0e05f56cc370bc4/asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590", size = 560404 }, - { url = "https://files.pythonhosted.org/packages/c8/a4/cec76b3389c4c5ff66301cd100fe88c318563ec8a520e0b2e792b5b84972/asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e", size = 621623 }, + { url = "https://files.pythonhosted.org/packages/3a/22/e20602e1218dc07692acf70d5b902be820168d6282e69ef0d3cb920dc36f/asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70", size = 670373, upload-time = "2024-10-20T00:29:55.165Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b3/0cf269a9d647852a95c06eb00b815d0b95a4eb4b55aa2d6ba680971733b9/asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3", size = 634745, upload-time = "2024-10-20T00:29:57.14Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6d/a4f31bf358ce8491d2a31bfe0d7bcf25269e80481e49de4d8616c4295a34/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33", size = 3512103, upload-time = "2024-10-20T00:29:58.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/19/139227a6e67f407b9c386cb594d9628c6c78c9024f26df87c912fabd4368/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4", size = 3592471, upload-time = "2024-10-20T00:30:00.354Z" }, + { url = "https://files.pythonhosted.org/packages/67/e4/ab3ca38f628f53f0fd28d3ff20edff1c975dd1cb22482e0061916b4b9a74/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4", size = 3496253, upload-time = "2024-10-20T00:30:02.794Z" }, + { url = "https://files.pythonhosted.org/packages/ef/5f/0bf65511d4eeac3a1f41c54034a492515a707c6edbc642174ae79034d3ba/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba", size = 3662720, upload-time = "2024-10-20T00:30:04.501Z" }, + { url = "https://files.pythonhosted.org/packages/e7/31/1513d5a6412b98052c3ed9158d783b1e09d0910f51fbe0e05f56cc370bc4/asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590", size = 560404, upload-time = "2024-10-20T00:30:06.537Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a4/cec76b3389c4c5ff66301cd100fe88c318563ec8a520e0b2e792b5b84972/asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e", size = 621623, upload-time = "2024-10-20T00:30:09.024Z" }, ] [[package]] name = "bcrypt" version = "4.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/5d/6d7433e0f3cd46ce0b43cd65e1db465ea024dbb8216fb2404e919c2ad77b/bcrypt-4.3.0.tar.gz", hash = "sha256:3a3fd2204178b6d2adcf09cb4f6426ffef54762577a7c9b54c159008cb288c18", size = 25697 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/2c/3d44e853d1fe969d229bd58d39ae6902b3d924af0e2b5a60d17d4b809ded/bcrypt-4.3.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281", size = 483719 }, - { url = "https://files.pythonhosted.org/packages/a1/e2/58ff6e2a22eca2e2cff5370ae56dba29d70b1ea6fc08ee9115c3ae367795/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb", size = 272001 }, - { url = "https://files.pythonhosted.org/packages/37/1f/c55ed8dbe994b1d088309e366749633c9eb90d139af3c0a50c102ba68a1a/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59e1aa0e2cd871b08ca146ed08445038f42ff75968c7ae50d2fdd7860ade2180", size = 277451 }, - { url = "https://files.pythonhosted.org/packages/d7/1c/794feb2ecf22fe73dcfb697ea7057f632061faceb7dcf0f155f3443b4d79/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:0042b2e342e9ae3d2ed22727c1262f76cc4f345683b5c1715f0250cf4277294f", size = 272792 }, - { url = "https://files.pythonhosted.org/packages/13/b7/0b289506a3f3598c2ae2bdfa0ea66969812ed200264e3f61df77753eee6d/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74a8d21a09f5e025a9a23e7c0fd2c7fe8e7503e4d356c0a2c1486ba010619f09", size = 289752 }, - { url = "https://files.pythonhosted.org/packages/dc/24/d0fb023788afe9e83cc118895a9f6c57e1044e7e1672f045e46733421fe6/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:0142b2cb84a009f8452c8c5a33ace5e3dfec4159e7735f5afe9a4d50a8ea722d", size = 277762 }, - { url = "https://files.pythonhosted.org/packages/e4/38/cde58089492e55ac4ef6c49fea7027600c84fd23f7520c62118c03b4625e/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:12fa6ce40cde3f0b899729dbd7d5e8811cb892d31b6f7d0334a1f37748b789fd", size = 272384 }, - { url = "https://files.pythonhosted.org/packages/de/6a/d5026520843490cfc8135d03012a413e4532a400e471e6188b01b2de853f/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:5bd3cca1f2aa5dbcf39e2aa13dd094ea181f48959e1071265de49cc2b82525af", size = 277329 }, - { url = "https://files.pythonhosted.org/packages/b3/a3/4fc5255e60486466c389e28c12579d2829b28a527360e9430b4041df4cf9/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:335a420cfd63fc5bc27308e929bee231c15c85cc4c496610ffb17923abf7f231", size = 305241 }, - { url = "https://files.pythonhosted.org/packages/c7/15/2b37bc07d6ce27cc94e5b10fd5058900eb8fb11642300e932c8c82e25c4a/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:0e30e5e67aed0187a1764911af023043b4542e70a7461ad20e837e94d23e1d6c", size = 309617 }, - { url = "https://files.pythonhosted.org/packages/5f/1f/99f65edb09e6c935232ba0430c8c13bb98cb3194b6d636e61d93fe60ac59/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b8d62290ebefd49ee0b3ce7500f5dbdcf13b81402c05f6dafab9a1e1b27212f", size = 335751 }, - { url = "https://files.pythonhosted.org/packages/00/1b/b324030c706711c99769988fcb694b3cb23f247ad39a7823a78e361bdbb8/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2ef6630e0ec01376f59a006dc72918b1bf436c3b571b80fa1968d775fa02fe7d", size = 355965 }, - { url = "https://files.pythonhosted.org/packages/aa/dd/20372a0579dd915dfc3b1cd4943b3bca431866fcb1dfdfd7518c3caddea6/bcrypt-4.3.0-cp313-cp313t-win32.whl", hash = "sha256:7a4be4cbf241afee43f1c3969b9103a41b40bcb3a3f467ab19f891d9bc4642e4", size = 155316 }, - { url = "https://files.pythonhosted.org/packages/6d/52/45d969fcff6b5577c2bf17098dc36269b4c02197d551371c023130c0f890/bcrypt-4.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c1949bf259a388863ced887c7861da1df681cb2388645766c89fdfd9004c669", size = 147752 }, - { url = "https://files.pythonhosted.org/packages/11/22/5ada0b9af72b60cbc4c9a399fdde4af0feaa609d27eb0adc61607997a3fa/bcrypt-4.3.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:f81b0ed2639568bf14749112298f9e4e2b28853dab50a8b357e31798686a036d", size = 498019 }, - { url = "https://files.pythonhosted.org/packages/b8/8c/252a1edc598dc1ce57905be173328eda073083826955ee3c97c7ff5ba584/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:864f8f19adbe13b7de11ba15d85d4a428c7e2f344bac110f667676a0ff84924b", size = 279174 }, - { url = "https://files.pythonhosted.org/packages/29/5b/4547d5c49b85f0337c13929f2ccbe08b7283069eea3550a457914fc078aa/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e36506d001e93bffe59754397572f21bb5dc7c83f54454c990c74a468cd589e", size = 283870 }, - { url = "https://files.pythonhosted.org/packages/be/21/7dbaf3fa1745cb63f776bb046e481fbababd7d344c5324eab47f5ca92dd2/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:842d08d75d9fe9fb94b18b071090220697f9f184d4547179b60734846461ed59", size = 279601 }, - { url = "https://files.pythonhosted.org/packages/6d/64/e042fc8262e971347d9230d9abbe70d68b0a549acd8611c83cebd3eaec67/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7c03296b85cb87db865d91da79bf63d5609284fc0cab9472fdd8367bbd830753", size = 297660 }, - { url = "https://files.pythonhosted.org/packages/50/b8/6294eb84a3fef3b67c69b4470fcdd5326676806bf2519cda79331ab3c3a9/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:62f26585e8b219cdc909b6a0069efc5e4267e25d4a3770a364ac58024f62a761", size = 284083 }, - { url = "https://files.pythonhosted.org/packages/62/e6/baff635a4f2c42e8788fe1b1633911c38551ecca9a749d1052d296329da6/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:beeefe437218a65322fbd0069eb437e7c98137e08f22c4660ac2dc795c31f8bb", size = 279237 }, - { url = "https://files.pythonhosted.org/packages/39/48/46f623f1b0c7dc2e5de0b8af5e6f5ac4cc26408ac33f3d424e5ad8da4a90/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:97eea7408db3a5bcce4a55d13245ab3fa566e23b4c67cd227062bb49e26c585d", size = 283737 }, - { url = "https://files.pythonhosted.org/packages/49/8b/70671c3ce9c0fca4a6cc3cc6ccbaa7e948875a2e62cbd146e04a4011899c/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:191354ebfe305e84f344c5964c7cd5f924a3bfc5d405c75ad07f232b6dffb49f", size = 312741 }, - { url = "https://files.pythonhosted.org/packages/27/fb/910d3a1caa2d249b6040a5caf9f9866c52114d51523ac2fb47578a27faee/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:41261d64150858eeb5ff43c753c4b216991e0ae16614a308a15d909503617732", size = 316472 }, - { url = "https://files.pythonhosted.org/packages/dc/cf/7cf3a05b66ce466cfb575dbbda39718d45a609daa78500f57fa9f36fa3c0/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:33752b1ba962ee793fa2b6321404bf20011fe45b9afd2a842139de3011898fef", size = 343606 }, - { url = "https://files.pythonhosted.org/packages/e3/b8/e970ecc6d7e355c0d892b7f733480f4aa8509f99b33e71550242cf0b7e63/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:50e6e80a4bfd23a25f5c05b90167c19030cf9f87930f7cb2eacb99f45d1c3304", size = 362867 }, - { url = "https://files.pythonhosted.org/packages/a9/97/8d3118efd8354c555a3422d544163f40d9f236be5b96c714086463f11699/bcrypt-4.3.0-cp38-abi3-win32.whl", hash = "sha256:67a561c4d9fb9465ec866177e7aebcad08fe23aaf6fbd692a6fab69088abfc51", size = 160589 }, - { url = "https://files.pythonhosted.org/packages/29/07/416f0b99f7f3997c69815365babbc2e8754181a4b1899d921b3c7d5b6f12/bcrypt-4.3.0-cp38-abi3-win_amd64.whl", hash = "sha256:584027857bc2843772114717a7490a37f68da563b3620f78a849bcb54dc11e62", size = 152794 }, - { url = "https://files.pythonhosted.org/packages/6e/c1/3fa0e9e4e0bfd3fd77eb8b52ec198fd6e1fd7e9402052e43f23483f956dd/bcrypt-4.3.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d3efb1157edebfd9128e4e46e2ac1a64e0c1fe46fb023158a407c7892b0f8c3", size = 498969 }, - { url = "https://files.pythonhosted.org/packages/ce/d4/755ce19b6743394787fbd7dff6bf271b27ee9b5912a97242e3caf125885b/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08bacc884fd302b611226c01014eca277d48f0a05187666bca23aac0dad6fe24", size = 279158 }, - { url = "https://files.pythonhosted.org/packages/9b/5d/805ef1a749c965c46b28285dfb5cd272a7ed9fa971f970435a5133250182/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6746e6fec103fcd509b96bacdfdaa2fbde9a553245dbada284435173a6f1aef", size = 284285 }, - { url = "https://files.pythonhosted.org/packages/ab/2b/698580547a4a4988e415721b71eb45e80c879f0fb04a62da131f45987b96/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:afe327968aaf13fc143a56a3360cb27d4ad0345e34da12c7290f1b00b8fe9a8b", size = 279583 }, - { url = "https://files.pythonhosted.org/packages/f2/87/62e1e426418204db520f955ffd06f1efd389feca893dad7095bf35612eec/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d9af79d322e735b1fc33404b5765108ae0ff232d4b54666d46730f8ac1a43676", size = 297896 }, - { url = "https://files.pythonhosted.org/packages/cb/c6/8fedca4c2ada1b6e889c52d2943b2f968d3427e5d65f595620ec4c06fa2f/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f1e3ffa1365e8702dc48c8b360fef8d7afeca482809c5e45e653af82ccd088c1", size = 284492 }, - { url = "https://files.pythonhosted.org/packages/4d/4d/c43332dcaaddb7710a8ff5269fcccba97ed3c85987ddaa808db084267b9a/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3004df1b323d10021fda07a813fd33e0fd57bef0e9a480bb143877f6cba996fe", size = 279213 }, - { url = "https://files.pythonhosted.org/packages/dc/7f/1e36379e169a7df3a14a1c160a49b7b918600a6008de43ff20d479e6f4b5/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:531457e5c839d8caea9b589a1bcfe3756b0547d7814e9ce3d437f17da75c32b0", size = 284162 }, - { url = "https://files.pythonhosted.org/packages/1c/0a/644b2731194b0d7646f3210dc4d80c7fee3ecb3a1f791a6e0ae6bb8684e3/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:17a854d9a7a476a89dcef6c8bd119ad23e0f82557afbd2c442777a16408e614f", size = 312856 }, - { url = "https://files.pythonhosted.org/packages/dc/62/2a871837c0bb6ab0c9a88bf54de0fc021a6a08832d4ea313ed92a669d437/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6fb1fd3ab08c0cbc6826a2e0447610c6f09e983a281b919ed721ad32236b8b23", size = 316726 }, - { url = "https://files.pythonhosted.org/packages/0c/a1/9898ea3faac0b156d457fd73a3cb9c2855c6fd063e44b8522925cdd8ce46/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e965a9c1e9a393b8005031ff52583cedc15b7884fce7deb8b0346388837d6cfe", size = 343664 }, - { url = "https://files.pythonhosted.org/packages/40/f2/71b4ed65ce38982ecdda0ff20c3ad1b15e71949c78b2c053df53629ce940/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:79e70b8342a33b52b55d93b3a59223a844962bef479f6a0ea318ebbcadf71505", size = 363128 }, - { url = "https://files.pythonhosted.org/packages/11/99/12f6a58eca6dea4be992d6c681b7ec9410a1d9f5cf368c61437e31daa879/bcrypt-4.3.0-cp39-abi3-win32.whl", hash = "sha256:b4d4e57f0a63fd0b358eb765063ff661328f69a04494427265950c71b992a39a", size = 160598 }, - { url = "https://files.pythonhosted.org/packages/a9/cf/45fb5261ece3e6b9817d3d82b2f343a505fd58674a92577923bc500bd1aa/bcrypt-4.3.0-cp39-abi3-win_amd64.whl", hash = "sha256:e53e074b120f2877a35cc6c736b8eb161377caae8925c17688bd46ba56daaa5b", size = 152799 }, +sdist = { url = "https://files.pythonhosted.org/packages/bb/5d/6d7433e0f3cd46ce0b43cd65e1db465ea024dbb8216fb2404e919c2ad77b/bcrypt-4.3.0.tar.gz", hash = "sha256:3a3fd2204178b6d2adcf09cb4f6426ffef54762577a7c9b54c159008cb288c18", size = 25697, upload-time = "2025-02-28T01:24:09.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/2c/3d44e853d1fe969d229bd58d39ae6902b3d924af0e2b5a60d17d4b809ded/bcrypt-4.3.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281", size = 483719, upload-time = "2025-02-28T01:22:34.539Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e2/58ff6e2a22eca2e2cff5370ae56dba29d70b1ea6fc08ee9115c3ae367795/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb", size = 272001, upload-time = "2025-02-28T01:22:38.078Z" }, + { url = "https://files.pythonhosted.org/packages/37/1f/c55ed8dbe994b1d088309e366749633c9eb90d139af3c0a50c102ba68a1a/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59e1aa0e2cd871b08ca146ed08445038f42ff75968c7ae50d2fdd7860ade2180", size = 277451, upload-time = "2025-02-28T01:22:40.787Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1c/794feb2ecf22fe73dcfb697ea7057f632061faceb7dcf0f155f3443b4d79/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:0042b2e342e9ae3d2ed22727c1262f76cc4f345683b5c1715f0250cf4277294f", size = 272792, upload-time = "2025-02-28T01:22:43.144Z" }, + { url = "https://files.pythonhosted.org/packages/13/b7/0b289506a3f3598c2ae2bdfa0ea66969812ed200264e3f61df77753eee6d/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74a8d21a09f5e025a9a23e7c0fd2c7fe8e7503e4d356c0a2c1486ba010619f09", size = 289752, upload-time = "2025-02-28T01:22:45.56Z" }, + { url = "https://files.pythonhosted.org/packages/dc/24/d0fb023788afe9e83cc118895a9f6c57e1044e7e1672f045e46733421fe6/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:0142b2cb84a009f8452c8c5a33ace5e3dfec4159e7735f5afe9a4d50a8ea722d", size = 277762, upload-time = "2025-02-28T01:22:47.023Z" }, + { url = "https://files.pythonhosted.org/packages/e4/38/cde58089492e55ac4ef6c49fea7027600c84fd23f7520c62118c03b4625e/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:12fa6ce40cde3f0b899729dbd7d5e8811cb892d31b6f7d0334a1f37748b789fd", size = 272384, upload-time = "2025-02-28T01:22:49.221Z" }, + { url = "https://files.pythonhosted.org/packages/de/6a/d5026520843490cfc8135d03012a413e4532a400e471e6188b01b2de853f/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:5bd3cca1f2aa5dbcf39e2aa13dd094ea181f48959e1071265de49cc2b82525af", size = 277329, upload-time = "2025-02-28T01:22:51.603Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a3/4fc5255e60486466c389e28c12579d2829b28a527360e9430b4041df4cf9/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:335a420cfd63fc5bc27308e929bee231c15c85cc4c496610ffb17923abf7f231", size = 305241, upload-time = "2025-02-28T01:22:53.283Z" }, + { url = "https://files.pythonhosted.org/packages/c7/15/2b37bc07d6ce27cc94e5b10fd5058900eb8fb11642300e932c8c82e25c4a/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:0e30e5e67aed0187a1764911af023043b4542e70a7461ad20e837e94d23e1d6c", size = 309617, upload-time = "2025-02-28T01:22:55.461Z" }, + { url = "https://files.pythonhosted.org/packages/5f/1f/99f65edb09e6c935232ba0430c8c13bb98cb3194b6d636e61d93fe60ac59/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b8d62290ebefd49ee0b3ce7500f5dbdcf13b81402c05f6dafab9a1e1b27212f", size = 335751, upload-time = "2025-02-28T01:22:57.81Z" }, + { url = "https://files.pythonhosted.org/packages/00/1b/b324030c706711c99769988fcb694b3cb23f247ad39a7823a78e361bdbb8/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2ef6630e0ec01376f59a006dc72918b1bf436c3b571b80fa1968d775fa02fe7d", size = 355965, upload-time = "2025-02-28T01:22:59.181Z" }, + { url = "https://files.pythonhosted.org/packages/aa/dd/20372a0579dd915dfc3b1cd4943b3bca431866fcb1dfdfd7518c3caddea6/bcrypt-4.3.0-cp313-cp313t-win32.whl", hash = "sha256:7a4be4cbf241afee43f1c3969b9103a41b40bcb3a3f467ab19f891d9bc4642e4", size = 155316, upload-time = "2025-02-28T01:23:00.763Z" }, + { url = "https://files.pythonhosted.org/packages/6d/52/45d969fcff6b5577c2bf17098dc36269b4c02197d551371c023130c0f890/bcrypt-4.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c1949bf259a388863ced887c7861da1df681cb2388645766c89fdfd9004c669", size = 147752, upload-time = "2025-02-28T01:23:02.908Z" }, + { url = "https://files.pythonhosted.org/packages/11/22/5ada0b9af72b60cbc4c9a399fdde4af0feaa609d27eb0adc61607997a3fa/bcrypt-4.3.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:f81b0ed2639568bf14749112298f9e4e2b28853dab50a8b357e31798686a036d", size = 498019, upload-time = "2025-02-28T01:23:05.838Z" }, + { url = "https://files.pythonhosted.org/packages/b8/8c/252a1edc598dc1ce57905be173328eda073083826955ee3c97c7ff5ba584/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:864f8f19adbe13b7de11ba15d85d4a428c7e2f344bac110f667676a0ff84924b", size = 279174, upload-time = "2025-02-28T01:23:07.274Z" }, + { url = "https://files.pythonhosted.org/packages/29/5b/4547d5c49b85f0337c13929f2ccbe08b7283069eea3550a457914fc078aa/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e36506d001e93bffe59754397572f21bb5dc7c83f54454c990c74a468cd589e", size = 283870, upload-time = "2025-02-28T01:23:09.151Z" }, + { url = "https://files.pythonhosted.org/packages/be/21/7dbaf3fa1745cb63f776bb046e481fbababd7d344c5324eab47f5ca92dd2/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:842d08d75d9fe9fb94b18b071090220697f9f184d4547179b60734846461ed59", size = 279601, upload-time = "2025-02-28T01:23:11.461Z" }, + { url = "https://files.pythonhosted.org/packages/6d/64/e042fc8262e971347d9230d9abbe70d68b0a549acd8611c83cebd3eaec67/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7c03296b85cb87db865d91da79bf63d5609284fc0cab9472fdd8367bbd830753", size = 297660, upload-time = "2025-02-28T01:23:12.989Z" }, + { url = "https://files.pythonhosted.org/packages/50/b8/6294eb84a3fef3b67c69b4470fcdd5326676806bf2519cda79331ab3c3a9/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:62f26585e8b219cdc909b6a0069efc5e4267e25d4a3770a364ac58024f62a761", size = 284083, upload-time = "2025-02-28T01:23:14.5Z" }, + { url = "https://files.pythonhosted.org/packages/62/e6/baff635a4f2c42e8788fe1b1633911c38551ecca9a749d1052d296329da6/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:beeefe437218a65322fbd0069eb437e7c98137e08f22c4660ac2dc795c31f8bb", size = 279237, upload-time = "2025-02-28T01:23:16.686Z" }, + { url = "https://files.pythonhosted.org/packages/39/48/46f623f1b0c7dc2e5de0b8af5e6f5ac4cc26408ac33f3d424e5ad8da4a90/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:97eea7408db3a5bcce4a55d13245ab3fa566e23b4c67cd227062bb49e26c585d", size = 283737, upload-time = "2025-02-28T01:23:18.897Z" }, + { url = "https://files.pythonhosted.org/packages/49/8b/70671c3ce9c0fca4a6cc3cc6ccbaa7e948875a2e62cbd146e04a4011899c/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:191354ebfe305e84f344c5964c7cd5f924a3bfc5d405c75ad07f232b6dffb49f", size = 312741, upload-time = "2025-02-28T01:23:21.041Z" }, + { url = "https://files.pythonhosted.org/packages/27/fb/910d3a1caa2d249b6040a5caf9f9866c52114d51523ac2fb47578a27faee/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:41261d64150858eeb5ff43c753c4b216991e0ae16614a308a15d909503617732", size = 316472, upload-time = "2025-02-28T01:23:23.183Z" }, + { url = "https://files.pythonhosted.org/packages/dc/cf/7cf3a05b66ce466cfb575dbbda39718d45a609daa78500f57fa9f36fa3c0/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:33752b1ba962ee793fa2b6321404bf20011fe45b9afd2a842139de3011898fef", size = 343606, upload-time = "2025-02-28T01:23:25.361Z" }, + { url = "https://files.pythonhosted.org/packages/e3/b8/e970ecc6d7e355c0d892b7f733480f4aa8509f99b33e71550242cf0b7e63/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:50e6e80a4bfd23a25f5c05b90167c19030cf9f87930f7cb2eacb99f45d1c3304", size = 362867, upload-time = "2025-02-28T01:23:26.875Z" }, + { url = "https://files.pythonhosted.org/packages/a9/97/8d3118efd8354c555a3422d544163f40d9f236be5b96c714086463f11699/bcrypt-4.3.0-cp38-abi3-win32.whl", hash = "sha256:67a561c4d9fb9465ec866177e7aebcad08fe23aaf6fbd692a6fab69088abfc51", size = 160589, upload-time = "2025-02-28T01:23:28.381Z" }, + { url = "https://files.pythonhosted.org/packages/29/07/416f0b99f7f3997c69815365babbc2e8754181a4b1899d921b3c7d5b6f12/bcrypt-4.3.0-cp38-abi3-win_amd64.whl", hash = "sha256:584027857bc2843772114717a7490a37f68da563b3620f78a849bcb54dc11e62", size = 152794, upload-time = "2025-02-28T01:23:30.187Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c1/3fa0e9e4e0bfd3fd77eb8b52ec198fd6e1fd7e9402052e43f23483f956dd/bcrypt-4.3.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d3efb1157edebfd9128e4e46e2ac1a64e0c1fe46fb023158a407c7892b0f8c3", size = 498969, upload-time = "2025-02-28T01:23:31.945Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d4/755ce19b6743394787fbd7dff6bf271b27ee9b5912a97242e3caf125885b/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08bacc884fd302b611226c01014eca277d48f0a05187666bca23aac0dad6fe24", size = 279158, upload-time = "2025-02-28T01:23:34.161Z" }, + { url = "https://files.pythonhosted.org/packages/9b/5d/805ef1a749c965c46b28285dfb5cd272a7ed9fa971f970435a5133250182/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6746e6fec103fcd509b96bacdfdaa2fbde9a553245dbada284435173a6f1aef", size = 284285, upload-time = "2025-02-28T01:23:35.765Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2b/698580547a4a4988e415721b71eb45e80c879f0fb04a62da131f45987b96/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:afe327968aaf13fc143a56a3360cb27d4ad0345e34da12c7290f1b00b8fe9a8b", size = 279583, upload-time = "2025-02-28T01:23:38.021Z" }, + { url = "https://files.pythonhosted.org/packages/f2/87/62e1e426418204db520f955ffd06f1efd389feca893dad7095bf35612eec/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d9af79d322e735b1fc33404b5765108ae0ff232d4b54666d46730f8ac1a43676", size = 297896, upload-time = "2025-02-28T01:23:39.575Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c6/8fedca4c2ada1b6e889c52d2943b2f968d3427e5d65f595620ec4c06fa2f/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f1e3ffa1365e8702dc48c8b360fef8d7afeca482809c5e45e653af82ccd088c1", size = 284492, upload-time = "2025-02-28T01:23:40.901Z" }, + { url = "https://files.pythonhosted.org/packages/4d/4d/c43332dcaaddb7710a8ff5269fcccba97ed3c85987ddaa808db084267b9a/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3004df1b323d10021fda07a813fd33e0fd57bef0e9a480bb143877f6cba996fe", size = 279213, upload-time = "2025-02-28T01:23:42.653Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/1e36379e169a7df3a14a1c160a49b7b918600a6008de43ff20d479e6f4b5/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:531457e5c839d8caea9b589a1bcfe3756b0547d7814e9ce3d437f17da75c32b0", size = 284162, upload-time = "2025-02-28T01:23:43.964Z" }, + { url = "https://files.pythonhosted.org/packages/1c/0a/644b2731194b0d7646f3210dc4d80c7fee3ecb3a1f791a6e0ae6bb8684e3/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:17a854d9a7a476a89dcef6c8bd119ad23e0f82557afbd2c442777a16408e614f", size = 312856, upload-time = "2025-02-28T01:23:46.011Z" }, + { url = "https://files.pythonhosted.org/packages/dc/62/2a871837c0bb6ab0c9a88bf54de0fc021a6a08832d4ea313ed92a669d437/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6fb1fd3ab08c0cbc6826a2e0447610c6f09e983a281b919ed721ad32236b8b23", size = 316726, upload-time = "2025-02-28T01:23:47.575Z" }, + { url = "https://files.pythonhosted.org/packages/0c/a1/9898ea3faac0b156d457fd73a3cb9c2855c6fd063e44b8522925cdd8ce46/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e965a9c1e9a393b8005031ff52583cedc15b7884fce7deb8b0346388837d6cfe", size = 343664, upload-time = "2025-02-28T01:23:49.059Z" }, + { url = "https://files.pythonhosted.org/packages/40/f2/71b4ed65ce38982ecdda0ff20c3ad1b15e71949c78b2c053df53629ce940/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:79e70b8342a33b52b55d93b3a59223a844962bef479f6a0ea318ebbcadf71505", size = 363128, upload-time = "2025-02-28T01:23:50.399Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/12f6a58eca6dea4be992d6c681b7ec9410a1d9f5cf368c61437e31daa879/bcrypt-4.3.0-cp39-abi3-win32.whl", hash = "sha256:b4d4e57f0a63fd0b358eb765063ff661328f69a04494427265950c71b992a39a", size = 160598, upload-time = "2025-02-28T01:23:51.775Z" }, + { url = "https://files.pythonhosted.org/packages/a9/cf/45fb5261ece3e6b9817d3d82b2f343a505fd58674a92577923bc500bd1aa/bcrypt-4.3.0-cp39-abi3-win_amd64.whl", hash = "sha256:e53e074b120f2877a35cc6c736b8eb161377caae8925c17688bd46ba56daaa5b", size = 152799, upload-time = "2025-02-28T01:23:53.139Z" }, ] [[package]] @@ -215,64 +215,64 @@ dependencies = [ { name = "soupsieve" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822 } +sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822, upload-time = "2025-09-29T10:05:42.613Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392 }, + { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392, upload-time = "2025-09-29T10:05:43.771Z" }, ] [[package]] name = "blinker" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 }, + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, ] [[package]] name = "boto3" -version = "1.40.68" +version = "1.40.69" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/3e/6c8ab966798f4e07651009ad08efc3ed4ffccf2662318790574695c740f7/boto3-1.40.68.tar.gz", hash = "sha256:c7994989e5bbba071b7c742adfba35773cf03e87f5d3f9f2b0a18c1664417b61", size = 111629 } +sdist = { url = "https://files.pythonhosted.org/packages/56/36/65d292d14261aedbb9a22e5bf194d84c119c889135b42448db646d06d76b/boto3-1.40.69.tar.gz", hash = "sha256:5273f6bac347331a87db809dff97d8736c50c3be19f2bb36ad08c5131c408976", size = 111628, upload-time = "2025-11-07T20:26:26.949Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/e6/b9df94d3a51ad658ef1974da6c0d7401b6aed7be50a2ee57bf1de1ef9517/boto3-1.40.68-py3-none-any.whl", hash = "sha256:4f08115e3a4d1e1056003e433d393e78c20da6af7753409992bb33fb69f04186", size = 139361 }, + { url = "https://files.pythonhosted.org/packages/4d/2f/65009a8d274cd9c7211807c1a07cce17203ffe76368e3ebc4ca03a7b79de/boto3-1.40.69-py3-none-any.whl", hash = "sha256:c3f710a1990c4be1c0db43b938743d4e404c7f1f06d5f1fa0c8e9b1cea4290b2", size = 139361, upload-time = "2025-11-07T20:26:24.522Z" }, ] [[package]] name = "botocore" -version = "1.40.68" +version = "1.40.69" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/df/b0300da4cc1fe3e37c8d7a44d835518004454c7d21b579fce9ef2cd691ce/botocore-1.40.68.tar.gz", hash = "sha256:28f41b463d9f012a711ee8b61d4e26cd14ee3b450b816d5dee849aa79155e856", size = 14435596 } +sdist = { url = "https://files.pythonhosted.org/packages/e2/73/42499b183ca5cef25c35338ad2636368b0ae2193654642756492e96ee906/botocore-1.40.69.tar.gz", hash = "sha256:df310ddc4d2de5543ba3df4e4b5f9907a2951896d63a9fbae115c26ca0976951", size = 14440352, upload-time = "2025-11-07T20:26:14.276Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/72/ac8123169ce48cb2eb593cd4c6a22e66d72bf8dc30fe75191a7669dd036d/botocore-1.40.68-py3-none-any.whl", hash = "sha256:9d514f9c9054e1af055f2cbe9e0d6771d407a600206d45a01b54d5f09538fecb", size = 14097634 }, + { url = "https://files.pythonhosted.org/packages/61/d6/bf2b91d4a92af6ee70e0689913414463a48cf51c0fc855c98b94bde8e7f3/botocore-1.40.69-py3-none-any.whl", hash = "sha256:5d810efeb9e18f91f32690642fa81ae60e482eefeea0d35ec72da2e3d924c1a5", size = 14103454, upload-time = "2025-11-07T20:26:09.486Z" }, ] [[package]] name = "cachetools" version = "5.5.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380 } +sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080 }, + { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, ] [[package]] name = "certifi" version = "2025.10.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519, upload-time = "2025-10-05T04:12:15.808Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286 }, + { url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286, upload-time = "2025-10-05T04:12:14.03Z" }, ] [[package]] @@ -282,83 +282,83 @@ 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 } -wheels = [ - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909 }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402 }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328 }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650 }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487 }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726 }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195 }, +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/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 = "charset-normalizer" version = "3.4.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091 }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936 }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180 }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346 }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874 }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076 }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601 }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376 }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825 }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583 }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366 }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300 }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465 }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404 }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092 }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408 }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746 }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889 }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641 }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779 }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035 }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542 }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524 }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395 }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680 }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045 }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687 }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014 }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044 }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940 }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104 }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743 }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402 }, +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, ] [[package]] @@ -368,18 +368,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943 } +sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295 }, + { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, ] [[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 } +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 }, + { 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]] @@ -389,70 +389,70 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "humanfriendly" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018 }, + { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" }, ] [[package]] name = "coverage" version = "7.11.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/89/12/3e2d2ec71796e0913178478e693a06af6a3bc9f7f9cb899bf85a426d8370/coverage-7.11.1.tar.gz", hash = "sha256:b4b3a072559578129a9e863082a2972a2abd8975bc0e2ec57da96afcd6580a8a", size = 814037 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/01/0c50c318f5e8f1a482da05d788d0ff06137803ed8fface4a1ba51e04b3ad/coverage-7.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:da9930594ca99d66eb6f613d7beba850db2f8dfa86810ee35ae24e4d5f2bb97d", size = 216920 }, - { url = "https://files.pythonhosted.org/packages/20/11/9f038e6c2baea968c377ab355b0d1d0a46b5f38985691bf51164e1b78c1f/coverage-7.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc47a280dc014220b0fc6e5f55082a3f51854faf08fd9635b8a4f341c46c77d3", size = 217301 }, - { url = "https://files.pythonhosted.org/packages/68/cd/9dcf93d81d0cddaa0bba90c3b4580e6f1ddf833918b816930d250cc553a4/coverage-7.11.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:74003324321bbf130939146886eddf92e48e616b5910215e79dea6edeb8ee7c8", size = 248277 }, - { url = "https://files.pythonhosted.org/packages/11/f5/b2c7c494046c9c783d3cac4c812fc24d6104dd36a7a598e7dd6fea3e7927/coverage-7.11.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:211f7996265daab60a8249af4ca6641b3080769cbedcffc42cc4841118f3a305", size = 250871 }, - { url = "https://files.pythonhosted.org/packages/a5/5a/b359649566954498aa17d7c98093182576d9e435ceb4ea917b3b48d56f86/coverage-7.11.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70619d194d8fea0cb028cb6bb9c85b519c7509c1d1feef1eea635183bc8ecd27", size = 252115 }, - { url = "https://files.pythonhosted.org/packages/f3/17/3cef1ede3739622950f0737605353b797ec564e70c9d254521b10f4b03ba/coverage-7.11.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e0208bb59d441cfa3321569040f8e455f9261256e0df776c5462a1e5a9b31e13", size = 248442 }, - { url = "https://files.pythonhosted.org/packages/5f/63/d5854c47ae42d9d18855329db6bc528f5b7f4f874257edb00cf8b483f9f8/coverage-7.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:545714d8765bda1c51f8b1c96e0b497886a054471c68211e76ef49dd1468587d", size = 250253 }, - { url = "https://files.pythonhosted.org/packages/48/e8/c7706f8a5358a59c18b489e7e19e83d6161b7c8bc60771f95920570c94a8/coverage-7.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d0a2b02c1e20158dd405054bcca87f91fd5b7605626aee87150819ea616edd67", size = 248217 }, - { url = "https://files.pythonhosted.org/packages/5b/c9/a2136dfb168eb09e2f6d9d6b6c986243fdc0b3866a9376adb263d3c3378b/coverage-7.11.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0f4aa986a4308a458e0fb572faa3eb3db2ea7ce294604064b25ab32b435a468", size = 248040 }, - { url = "https://files.pythonhosted.org/packages/18/9a/a63991c0608ddc6adf65e6f43124951aaf36bd79f41937b028120b8268ea/coverage-7.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d51cc6687e8bbfd1e041f52baed0f979cd592242cf50bf18399a7e03afc82d88", size = 249801 }, - { url = "https://files.pythonhosted.org/packages/84/19/947acf7c0c6e90e4ec3abf474133ed36d94407d07e36eafdfd3acb59fee9/coverage-7.11.1-cp313-cp313-win32.whl", hash = "sha256:1b3067db3afe6deeca2b2c9f0ec23820d5f1bd152827acfadf24de145dfc5f66", size = 219430 }, - { url = "https://files.pythonhosted.org/packages/35/54/36fef7afb3884450c7b6d494fcabe2fab7c669d547c800ca30f41c1dc212/coverage-7.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:39a4c44b0cd40e3c9d89b2b7303ebd6ab9ae8a63f9e9a8c4d65a181a0b33aebe", size = 220239 }, - { url = "https://files.pythonhosted.org/packages/d3/dc/7d38bb99e8e69200b7dd5de15507226bd90eac102dfc7cc891b9934cdc76/coverage-7.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:a2e3560bf82fa8169a577e054cbbc29888699526063fee26ea59ea2627fd6e73", size = 218868 }, - { url = "https://files.pythonhosted.org/packages/36/c6/d1ff54fbd6bcad42dbcfd13b417e636ef84aae194353b1ef3361700f2525/coverage-7.11.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47a4f362a10285897ab3aa7a4b37d28213a4f2626823923613d6d7a3584dd79a", size = 217615 }, - { url = "https://files.pythonhosted.org/packages/73/f9/6ed59e7cf1488d6f975e5b14ef836f5e537913523e92175135f8518a83ce/coverage-7.11.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0df35fa7419ef571db9dacd50b0517bc54dbfe37eb94043b5fc3540bff276acd", size = 217960 }, - { url = "https://files.pythonhosted.org/packages/c4/74/2dab1dc2ebe16f074f80ae483b0f45faf278d102be703ac01b32cd85b6c3/coverage-7.11.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e1a2c621d341c9d56f7917e56fbb56be4f73fe0d0e8dae28352fb095060fd467", size = 259262 }, - { url = "https://files.pythonhosted.org/packages/15/49/eccfe039663e29a50a54b0c2c8d076acd174d7ac50d018ef8a5b1c37c8dc/coverage-7.11.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c354b111be9b2234d9573d75dd30ca4e414b7659c730e477e89be4f620b3fb5", size = 261326 }, - { url = "https://files.pythonhosted.org/packages/f0/bb/2b829aa23fd5ee8318e33cc02a606eb09900921291497963adc3f06af8bb/coverage-7.11.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4589bd44698728f600233fb2881014c9b8ec86637ef454c00939e779661dbe7e", size = 263758 }, - { url = "https://files.pythonhosted.org/packages/ac/03/d44c3d70e5da275caf2cad2071da6b425412fbcb1d1d5a81f1f89b45e3f1/coverage-7.11.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c6956fc8754f2309131230272a7213a483a32ecbe29e2b9316d808a28f2f8ea1", size = 258444 }, - { url = "https://files.pythonhosted.org/packages/a9/c1/cf61d9f46ae088774c65dd3387a15dfbc72de90c1f6e105025e9eda19b42/coverage-7.11.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63926a97ed89dc6a087369b92dcb8b9a94cead46c08b33a7f1f4818cd8b6a3c3", size = 261335 }, - { url = "https://files.pythonhosted.org/packages/95/9a/b3299bb14f11f2364d78a2b9704491b15395e757af6116694731ce4e5834/coverage-7.11.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f5311ba00c53a7fb2b293fdc1f478b7286fe2a845a7ba9cda053f6e98178f0b4", size = 258951 }, - { url = "https://files.pythonhosted.org/packages/3f/a3/73cb2763e59f14ba6d8d6444b1f640a9be2242bfb59b7e50581c695db7ff/coverage-7.11.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:31bf5ffad84c974f9e72ac53493350f36b6fa396109159ec704210698f12860b", size = 257840 }, - { url = "https://files.pythonhosted.org/packages/85/db/482e72589a952027e238ffa3a15f192c552e0685fd0c5220ad05b5f17d56/coverage-7.11.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:227ee59fbc4a8c57a7383a1d7af6ca94a78ae3beee4045f38684548a8479a65b", size = 260040 }, - { url = "https://files.pythonhosted.org/packages/18/a1/b931d3ee099c2dca8e9ea56c07ae84c0f91562f7bbbcccab8c91b3474ef1/coverage-7.11.1-cp313-cp313t-win32.whl", hash = "sha256:a447d97b3ce680bb1da2e6bd822ebb71be6a1fb77ce2c2ad2fe4bd8aacec3058", size = 220102 }, - { url = "https://files.pythonhosted.org/packages/9a/53/b553b7bfa6207def4918f0cb72884c844fa4c3f1566e58fbb4f34e54cdc5/coverage-7.11.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d6d11180437c67bde2248563a42b8e5bbf85c8df78fae13bf818ad17bfb15f02", size = 221166 }, - { url = "https://files.pythonhosted.org/packages/6b/45/1c1d58b3ed585598764bd2fe41fcf60ccafe15973ad621c322ba52e22d32/coverage-7.11.1-cp313-cp313t-win_arm64.whl", hash = "sha256:1e19a4c43d612760c6f7190411fb157e2d8a6dde00c91b941d43203bd3b17f6f", size = 219439 }, - { url = "https://files.pythonhosted.org/packages/d9/c2/ac2c3417eaa4de1361036ebbc7da664242b274b2e00c4b4a1cfc7b29920b/coverage-7.11.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0305463c45c5f21f0396cd5028de92b1f1387e2e0756a85dd3147daa49f7a674", size = 216967 }, - { url = "https://files.pythonhosted.org/packages/5e/a3/afef455d03c468ee303f9df9a6f407e8bea64cd576fca914ff888faf52ca/coverage-7.11.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fa4d468d5efa1eb6e3062be8bd5f45cbf28257a37b71b969a8c1da2652dfec77", size = 217298 }, - { url = "https://files.pythonhosted.org/packages/9d/59/6e2fb3fb58637001132dc32228b4fb5b332d75d12f1353cb00fe084ee0ba/coverage-7.11.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d2b2f5fc8fe383cbf2d5c77d6c4b2632ede553bc0afd0cdc910fa5390046c290", size = 248337 }, - { url = "https://files.pythonhosted.org/packages/1d/5e/ce442bab963e3388658da8bde6ddbd0a15beda230afafaa25e3c487dc391/coverage-7.11.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bde6488c1ad509f4fb1a4f9960fd003d5a94adef61e226246f9699befbab3276", size = 250853 }, - { url = "https://files.pythonhosted.org/packages/d1/2f/43f94557924ca9b64e09f1c3876da4eec44a05a41e27b8a639d899716c0e/coverage-7.11.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a69e0d6fa0b920fe6706a898c52955ec5bcfa7e45868215159f45fd87ea6da7c", size = 252190 }, - { url = "https://files.pythonhosted.org/packages/8c/fa/a04e769b92bc5628d4bd909dcc3c8219efe5e49f462e29adc43e198ecfde/coverage-7.11.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:976e51e4a549b80e4639eda3a53e95013a14ff6ad69bb58ed604d34deb0e774c", size = 248335 }, - { url = "https://files.pythonhosted.org/packages/99/d0/b98ab5d2abe425c71117a7c690ead697a0b32b83256bf0f566c726b7f77b/coverage-7.11.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d61fcc4d384c82971a3d9cf00d0872881f9ded19404c714d6079b7a4547e2955", size = 250209 }, - { url = "https://files.pythonhosted.org/packages/9c/3f/b9c4fbd2e6d1b64098f99fb68df7f7c1b3e0a0968d24025adb24f359cdec/coverage-7.11.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:284c5df762b533fae3ebd764e3b81c20c1c9648d93ef34469759cb4e3dfe13d0", size = 248163 }, - { url = "https://files.pythonhosted.org/packages/08/fc/3e4d54fb6368b0628019eefd897fc271badbd025410fd5421a65fb58758f/coverage-7.11.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:bab32cb1d4ad2ac6dcc4e17eee5fa136c2a1d14ae914e4bce6c8b78273aece3c", size = 247983 }, - { url = "https://files.pythonhosted.org/packages/b9/4a/a5700764a12e932b35afdddb2f59adbca289c1689455d06437f609f3ef35/coverage-7.11.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:36f2fed9ce392ca450fb4e283900d0b41f05c8c5db674d200f471498be3ce747", size = 249646 }, - { url = "https://files.pythonhosted.org/packages/0e/2c/45ed33d9e80a1cc9b44b4bd535d44c154d3204671c65abd90ec1e99522a2/coverage-7.11.1-cp314-cp314-win32.whl", hash = "sha256:853136cecb92a5ba1cc8f61ec6ffa62ca3c88b4b386a6c835f8b833924f9a8c5", size = 219700 }, - { url = "https://files.pythonhosted.org/packages/90/d7/5845597360f6434af1290118ebe114642865f45ce47e7e822d9c07b371be/coverage-7.11.1-cp314-cp314-win_amd64.whl", hash = "sha256:77443d39143e20927259a61da0c95d55ffc31cf43086b8f0f11a92da5260d592", size = 220516 }, - { url = "https://files.pythonhosted.org/packages/ae/d0/d311a06f9cf7a48a98ffcfd0c57db0dcab6da46e75c439286a50dc648161/coverage-7.11.1-cp314-cp314-win_arm64.whl", hash = "sha256:829acb88fa47591a64bf5197e96a931ce9d4b3634c7f81a224ba3319623cdf6c", size = 219091 }, - { url = "https://files.pythonhosted.org/packages/a7/3d/c6a84da4fa9b840933045b19dd19d17b892f3f2dd1612903260291416dba/coverage-7.11.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2ad1fe321d9522ea14399de83e75a11fb6a8887930c3679feb383301c28070d9", size = 217700 }, - { url = "https://files.pythonhosted.org/packages/94/10/a4fc5022017dd7ac682dc423849c241dfbdad31734b8f96060d84e70b587/coverage-7.11.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f69c332f0c3d1357c74decc9b1843fcd428cf9221bf196a20ad22aa1db3e1b6c", size = 217968 }, - { url = "https://files.pythonhosted.org/packages/59/2d/a554cd98924d296de5816413280ac3b09e42a05fb248d66f8d474d321938/coverage-7.11.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:576baeea4eebde684bf6c91c01e97171c8015765c8b2cfd4022a42b899897811", size = 259334 }, - { url = "https://files.pythonhosted.org/packages/05/98/d484cb659ec33958ca96b6f03438f56edc23b239d1ad0417b7a97fc1848a/coverage-7.11.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:28ad84c694fa86084cfd3c1eab4149844b8cb95bd8e5cbfc4a647f3ee2cce2b3", size = 261445 }, - { url = "https://files.pythonhosted.org/packages/f3/fa/920cba122cc28f4557c0507f8bd7c6e527ebcc537d0309186f66464a8fd9/coverage-7.11.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b1043ff958f09fc3f552c014d599f3c6b7088ba97d7bc1bd1cce8603cd75b520", size = 263858 }, - { url = "https://files.pythonhosted.org/packages/2a/a0/036397bdbee0f3bd46c2e26fdfbb1a61b2140bf9059240c37b61149047fa/coverage-7.11.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c6681add5060c2742dafcf29826dff1ff8eef889a3b03390daeed84361c428bd", size = 258381 }, - { url = "https://files.pythonhosted.org/packages/b6/61/2533926eb8990f182eb287f4873216c8ca530cc47241144aabf46fe80abe/coverage-7.11.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:773419b225ec9a75caa1e941dd0c83a91b92c2b525269e44e6ee3e4c630607db", size = 261321 }, - { url = "https://files.pythonhosted.org/packages/32/6e/618f7e203a998e4f6b8a0fa395744a416ad2adbcdc3735bc19466456718a/coverage-7.11.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a9cb272a0e0157dbb9b2fd0b201b759bd378a1a6138a16536c025c2ce4f7643b", size = 258933 }, - { url = "https://files.pythonhosted.org/packages/22/40/6b1c27f772cb08a14a338647ead1254a57ee9dabbb4cacbc15df7f278741/coverage-7.11.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:e09adb2a7811dc75998eef68f47599cf699e2b62eed09c9fefaeb290b3920f34", size = 257756 }, - { url = "https://files.pythonhosted.org/packages/73/07/f9cd12f71307a785ea15b009c8d8cc2543e4a867bd04b8673843970b6b43/coverage-7.11.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1335fa8c2a2fea49924d97e1e3500cfe8d7c849f5369f26bb7559ad4259ccfab", size = 260086 }, - { url = "https://files.pythonhosted.org/packages/34/02/31c5394f6f5d72a466966bcfdb61ce5a19862d452816d6ffcbb44add16ee/coverage-7.11.1-cp314-cp314t-win32.whl", hash = "sha256:4782d71d2a4fa7cef95e853b7097c8bbead4dbd0e6f9c7152a6b11a194b794db", size = 220483 }, - { url = "https://files.pythonhosted.org/packages/7f/96/81e1ef5fbfd5090113a96e823dbe055e4c58d96ca73b1fb0ad9d26f9ec36/coverage-7.11.1-cp314-cp314t-win_amd64.whl", hash = "sha256:939f45e66eceb63c75e8eb8fc58bb7077c00f1a41b0e15c6ef02334a933cfe93", size = 221592 }, - { url = "https://files.pythonhosted.org/packages/38/7a/a5d050de44951ac453a2046a0f3fb5471a4a557f0c914d00db27d543d94c/coverage-7.11.1-cp314-cp314t-win_arm64.whl", hash = "sha256:01c575bdbef35e3f023b50a146e9a75c53816e4f2569109458155cd2315f87d9", size = 219627 }, - { url = "https://files.pythonhosted.org/packages/76/32/bd9f48c28e23b2f08946f8e83983617b00619f5538dbd7e1045fa7e88c00/coverage-7.11.1-py3-none-any.whl", hash = "sha256:0fa848acb5f1da24765cee840e1afe9232ac98a8f9431c6112c15b34e880b9e8", size = 208689 }, +sdist = { url = "https://files.pythonhosted.org/packages/89/12/3e2d2ec71796e0913178478e693a06af6a3bc9f7f9cb899bf85a426d8370/coverage-7.11.1.tar.gz", hash = "sha256:b4b3a072559578129a9e863082a2972a2abd8975bc0e2ec57da96afcd6580a8a", size = 814037, upload-time = "2025-11-07T10:52:41.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/01/0c50c318f5e8f1a482da05d788d0ff06137803ed8fface4a1ba51e04b3ad/coverage-7.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:da9930594ca99d66eb6f613d7beba850db2f8dfa86810ee35ae24e4d5f2bb97d", size = 216920, upload-time = "2025-11-07T10:50:55.992Z" }, + { url = "https://files.pythonhosted.org/packages/20/11/9f038e6c2baea968c377ab355b0d1d0a46b5f38985691bf51164e1b78c1f/coverage-7.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc47a280dc014220b0fc6e5f55082a3f51854faf08fd9635b8a4f341c46c77d3", size = 217301, upload-time = "2025-11-07T10:50:57.609Z" }, + { url = "https://files.pythonhosted.org/packages/68/cd/9dcf93d81d0cddaa0bba90c3b4580e6f1ddf833918b816930d250cc553a4/coverage-7.11.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:74003324321bbf130939146886eddf92e48e616b5910215e79dea6edeb8ee7c8", size = 248277, upload-time = "2025-11-07T10:50:59.442Z" }, + { url = "https://files.pythonhosted.org/packages/11/f5/b2c7c494046c9c783d3cac4c812fc24d6104dd36a7a598e7dd6fea3e7927/coverage-7.11.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:211f7996265daab60a8249af4ca6641b3080769cbedcffc42cc4841118f3a305", size = 250871, upload-time = "2025-11-07T10:51:01.094Z" }, + { url = "https://files.pythonhosted.org/packages/a5/5a/b359649566954498aa17d7c98093182576d9e435ceb4ea917b3b48d56f86/coverage-7.11.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70619d194d8fea0cb028cb6bb9c85b519c7509c1d1feef1eea635183bc8ecd27", size = 252115, upload-time = "2025-11-07T10:51:03.087Z" }, + { url = "https://files.pythonhosted.org/packages/f3/17/3cef1ede3739622950f0737605353b797ec564e70c9d254521b10f4b03ba/coverage-7.11.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e0208bb59d441cfa3321569040f8e455f9261256e0df776c5462a1e5a9b31e13", size = 248442, upload-time = "2025-11-07T10:51:04.888Z" }, + { url = "https://files.pythonhosted.org/packages/5f/63/d5854c47ae42d9d18855329db6bc528f5b7f4f874257edb00cf8b483f9f8/coverage-7.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:545714d8765bda1c51f8b1c96e0b497886a054471c68211e76ef49dd1468587d", size = 250253, upload-time = "2025-11-07T10:51:06.515Z" }, + { url = "https://files.pythonhosted.org/packages/48/e8/c7706f8a5358a59c18b489e7e19e83d6161b7c8bc60771f95920570c94a8/coverage-7.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d0a2b02c1e20158dd405054bcca87f91fd5b7605626aee87150819ea616edd67", size = 248217, upload-time = "2025-11-07T10:51:08.405Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c9/a2136dfb168eb09e2f6d9d6b6c986243fdc0b3866a9376adb263d3c3378b/coverage-7.11.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0f4aa986a4308a458e0fb572faa3eb3db2ea7ce294604064b25ab32b435a468", size = 248040, upload-time = "2025-11-07T10:51:10.626Z" }, + { url = "https://files.pythonhosted.org/packages/18/9a/a63991c0608ddc6adf65e6f43124951aaf36bd79f41937b028120b8268ea/coverage-7.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d51cc6687e8bbfd1e041f52baed0f979cd592242cf50bf18399a7e03afc82d88", size = 249801, upload-time = "2025-11-07T10:51:12.63Z" }, + { url = "https://files.pythonhosted.org/packages/84/19/947acf7c0c6e90e4ec3abf474133ed36d94407d07e36eafdfd3acb59fee9/coverage-7.11.1-cp313-cp313-win32.whl", hash = "sha256:1b3067db3afe6deeca2b2c9f0ec23820d5f1bd152827acfadf24de145dfc5f66", size = 219430, upload-time = "2025-11-07T10:51:14.329Z" }, + { url = "https://files.pythonhosted.org/packages/35/54/36fef7afb3884450c7b6d494fcabe2fab7c669d547c800ca30f41c1dc212/coverage-7.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:39a4c44b0cd40e3c9d89b2b7303ebd6ab9ae8a63f9e9a8c4d65a181a0b33aebe", size = 220239, upload-time = "2025-11-07T10:51:16.418Z" }, + { url = "https://files.pythonhosted.org/packages/d3/dc/7d38bb99e8e69200b7dd5de15507226bd90eac102dfc7cc891b9934cdc76/coverage-7.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:a2e3560bf82fa8169a577e054cbbc29888699526063fee26ea59ea2627fd6e73", size = 218868, upload-time = "2025-11-07T10:51:18.186Z" }, + { url = "https://files.pythonhosted.org/packages/36/c6/d1ff54fbd6bcad42dbcfd13b417e636ef84aae194353b1ef3361700f2525/coverage-7.11.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47a4f362a10285897ab3aa7a4b37d28213a4f2626823923613d6d7a3584dd79a", size = 217615, upload-time = "2025-11-07T10:51:21.065Z" }, + { url = "https://files.pythonhosted.org/packages/73/f9/6ed59e7cf1488d6f975e5b14ef836f5e537913523e92175135f8518a83ce/coverage-7.11.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0df35fa7419ef571db9dacd50b0517bc54dbfe37eb94043b5fc3540bff276acd", size = 217960, upload-time = "2025-11-07T10:51:22.797Z" }, + { url = "https://files.pythonhosted.org/packages/c4/74/2dab1dc2ebe16f074f80ae483b0f45faf278d102be703ac01b32cd85b6c3/coverage-7.11.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e1a2c621d341c9d56f7917e56fbb56be4f73fe0d0e8dae28352fb095060fd467", size = 259262, upload-time = "2025-11-07T10:51:24.467Z" }, + { url = "https://files.pythonhosted.org/packages/15/49/eccfe039663e29a50a54b0c2c8d076acd174d7ac50d018ef8a5b1c37c8dc/coverage-7.11.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c354b111be9b2234d9573d75dd30ca4e414b7659c730e477e89be4f620b3fb5", size = 261326, upload-time = "2025-11-07T10:51:26.232Z" }, + { url = "https://files.pythonhosted.org/packages/f0/bb/2b829aa23fd5ee8318e33cc02a606eb09900921291497963adc3f06af8bb/coverage-7.11.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4589bd44698728f600233fb2881014c9b8ec86637ef454c00939e779661dbe7e", size = 263758, upload-time = "2025-11-07T10:51:27.912Z" }, + { url = "https://files.pythonhosted.org/packages/ac/03/d44c3d70e5da275caf2cad2071da6b425412fbcb1d1d5a81f1f89b45e3f1/coverage-7.11.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c6956fc8754f2309131230272a7213a483a32ecbe29e2b9316d808a28f2f8ea1", size = 258444, upload-time = "2025-11-07T10:51:30.107Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c1/cf61d9f46ae088774c65dd3387a15dfbc72de90c1f6e105025e9eda19b42/coverage-7.11.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63926a97ed89dc6a087369b92dcb8b9a94cead46c08b33a7f1f4818cd8b6a3c3", size = 261335, upload-time = "2025-11-07T10:51:31.814Z" }, + { url = "https://files.pythonhosted.org/packages/95/9a/b3299bb14f11f2364d78a2b9704491b15395e757af6116694731ce4e5834/coverage-7.11.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f5311ba00c53a7fb2b293fdc1f478b7286fe2a845a7ba9cda053f6e98178f0b4", size = 258951, upload-time = "2025-11-07T10:51:33.925Z" }, + { url = "https://files.pythonhosted.org/packages/3f/a3/73cb2763e59f14ba6d8d6444b1f640a9be2242bfb59b7e50581c695db7ff/coverage-7.11.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:31bf5ffad84c974f9e72ac53493350f36b6fa396109159ec704210698f12860b", size = 257840, upload-time = "2025-11-07T10:51:36.092Z" }, + { url = "https://files.pythonhosted.org/packages/85/db/482e72589a952027e238ffa3a15f192c552e0685fd0c5220ad05b5f17d56/coverage-7.11.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:227ee59fbc4a8c57a7383a1d7af6ca94a78ae3beee4045f38684548a8479a65b", size = 260040, upload-time = "2025-11-07T10:51:38.277Z" }, + { url = "https://files.pythonhosted.org/packages/18/a1/b931d3ee099c2dca8e9ea56c07ae84c0f91562f7bbbcccab8c91b3474ef1/coverage-7.11.1-cp313-cp313t-win32.whl", hash = "sha256:a447d97b3ce680bb1da2e6bd822ebb71be6a1fb77ce2c2ad2fe4bd8aacec3058", size = 220102, upload-time = "2025-11-07T10:51:40.017Z" }, + { url = "https://files.pythonhosted.org/packages/9a/53/b553b7bfa6207def4918f0cb72884c844fa4c3f1566e58fbb4f34e54cdc5/coverage-7.11.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d6d11180437c67bde2248563a42b8e5bbf85c8df78fae13bf818ad17bfb15f02", size = 221166, upload-time = "2025-11-07T10:51:41.921Z" }, + { url = "https://files.pythonhosted.org/packages/6b/45/1c1d58b3ed585598764bd2fe41fcf60ccafe15973ad621c322ba52e22d32/coverage-7.11.1-cp313-cp313t-win_arm64.whl", hash = "sha256:1e19a4c43d612760c6f7190411fb157e2d8a6dde00c91b941d43203bd3b17f6f", size = 219439, upload-time = "2025-11-07T10:51:43.753Z" }, + { url = "https://files.pythonhosted.org/packages/d9/c2/ac2c3417eaa4de1361036ebbc7da664242b274b2e00c4b4a1cfc7b29920b/coverage-7.11.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0305463c45c5f21f0396cd5028de92b1f1387e2e0756a85dd3147daa49f7a674", size = 216967, upload-time = "2025-11-07T10:51:45.55Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a3/afef455d03c468ee303f9df9a6f407e8bea64cd576fca914ff888faf52ca/coverage-7.11.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fa4d468d5efa1eb6e3062be8bd5f45cbf28257a37b71b969a8c1da2652dfec77", size = 217298, upload-time = "2025-11-07T10:51:47.31Z" }, + { url = "https://files.pythonhosted.org/packages/9d/59/6e2fb3fb58637001132dc32228b4fb5b332d75d12f1353cb00fe084ee0ba/coverage-7.11.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d2b2f5fc8fe383cbf2d5c77d6c4b2632ede553bc0afd0cdc910fa5390046c290", size = 248337, upload-time = "2025-11-07T10:51:49.48Z" }, + { url = "https://files.pythonhosted.org/packages/1d/5e/ce442bab963e3388658da8bde6ddbd0a15beda230afafaa25e3c487dc391/coverage-7.11.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bde6488c1ad509f4fb1a4f9960fd003d5a94adef61e226246f9699befbab3276", size = 250853, upload-time = "2025-11-07T10:51:51.215Z" }, + { url = "https://files.pythonhosted.org/packages/d1/2f/43f94557924ca9b64e09f1c3876da4eec44a05a41e27b8a639d899716c0e/coverage-7.11.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a69e0d6fa0b920fe6706a898c52955ec5bcfa7e45868215159f45fd87ea6da7c", size = 252190, upload-time = "2025-11-07T10:51:53.262Z" }, + { url = "https://files.pythonhosted.org/packages/8c/fa/a04e769b92bc5628d4bd909dcc3c8219efe5e49f462e29adc43e198ecfde/coverage-7.11.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:976e51e4a549b80e4639eda3a53e95013a14ff6ad69bb58ed604d34deb0e774c", size = 248335, upload-time = "2025-11-07T10:51:55.388Z" }, + { url = "https://files.pythonhosted.org/packages/99/d0/b98ab5d2abe425c71117a7c690ead697a0b32b83256bf0f566c726b7f77b/coverage-7.11.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d61fcc4d384c82971a3d9cf00d0872881f9ded19404c714d6079b7a4547e2955", size = 250209, upload-time = "2025-11-07T10:51:57.263Z" }, + { url = "https://files.pythonhosted.org/packages/9c/3f/b9c4fbd2e6d1b64098f99fb68df7f7c1b3e0a0968d24025adb24f359cdec/coverage-7.11.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:284c5df762b533fae3ebd764e3b81c20c1c9648d93ef34469759cb4e3dfe13d0", size = 248163, upload-time = "2025-11-07T10:51:59.014Z" }, + { url = "https://files.pythonhosted.org/packages/08/fc/3e4d54fb6368b0628019eefd897fc271badbd025410fd5421a65fb58758f/coverage-7.11.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:bab32cb1d4ad2ac6dcc4e17eee5fa136c2a1d14ae914e4bce6c8b78273aece3c", size = 247983, upload-time = "2025-11-07T10:52:01.027Z" }, + { url = "https://files.pythonhosted.org/packages/b9/4a/a5700764a12e932b35afdddb2f59adbca289c1689455d06437f609f3ef35/coverage-7.11.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:36f2fed9ce392ca450fb4e283900d0b41f05c8c5db674d200f471498be3ce747", size = 249646, upload-time = "2025-11-07T10:52:02.856Z" }, + { url = "https://files.pythonhosted.org/packages/0e/2c/45ed33d9e80a1cc9b44b4bd535d44c154d3204671c65abd90ec1e99522a2/coverage-7.11.1-cp314-cp314-win32.whl", hash = "sha256:853136cecb92a5ba1cc8f61ec6ffa62ca3c88b4b386a6c835f8b833924f9a8c5", size = 219700, upload-time = "2025-11-07T10:52:05.05Z" }, + { url = "https://files.pythonhosted.org/packages/90/d7/5845597360f6434af1290118ebe114642865f45ce47e7e822d9c07b371be/coverage-7.11.1-cp314-cp314-win_amd64.whl", hash = "sha256:77443d39143e20927259a61da0c95d55ffc31cf43086b8f0f11a92da5260d592", size = 220516, upload-time = "2025-11-07T10:52:07.259Z" }, + { url = "https://files.pythonhosted.org/packages/ae/d0/d311a06f9cf7a48a98ffcfd0c57db0dcab6da46e75c439286a50dc648161/coverage-7.11.1-cp314-cp314-win_arm64.whl", hash = "sha256:829acb88fa47591a64bf5197e96a931ce9d4b3634c7f81a224ba3319623cdf6c", size = 219091, upload-time = "2025-11-07T10:52:09.216Z" }, + { url = "https://files.pythonhosted.org/packages/a7/3d/c6a84da4fa9b840933045b19dd19d17b892f3f2dd1612903260291416dba/coverage-7.11.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2ad1fe321d9522ea14399de83e75a11fb6a8887930c3679feb383301c28070d9", size = 217700, upload-time = "2025-11-07T10:52:11.348Z" }, + { url = "https://files.pythonhosted.org/packages/94/10/a4fc5022017dd7ac682dc423849c241dfbdad31734b8f96060d84e70b587/coverage-7.11.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f69c332f0c3d1357c74decc9b1843fcd428cf9221bf196a20ad22aa1db3e1b6c", size = 217968, upload-time = "2025-11-07T10:52:13.203Z" }, + { url = "https://files.pythonhosted.org/packages/59/2d/a554cd98924d296de5816413280ac3b09e42a05fb248d66f8d474d321938/coverage-7.11.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:576baeea4eebde684bf6c91c01e97171c8015765c8b2cfd4022a42b899897811", size = 259334, upload-time = "2025-11-07T10:52:15.079Z" }, + { url = "https://files.pythonhosted.org/packages/05/98/d484cb659ec33958ca96b6f03438f56edc23b239d1ad0417b7a97fc1848a/coverage-7.11.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:28ad84c694fa86084cfd3c1eab4149844b8cb95bd8e5cbfc4a647f3ee2cce2b3", size = 261445, upload-time = "2025-11-07T10:52:17.134Z" }, + { url = "https://files.pythonhosted.org/packages/f3/fa/920cba122cc28f4557c0507f8bd7c6e527ebcc537d0309186f66464a8fd9/coverage-7.11.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b1043ff958f09fc3f552c014d599f3c6b7088ba97d7bc1bd1cce8603cd75b520", size = 263858, upload-time = "2025-11-07T10:52:19.836Z" }, + { url = "https://files.pythonhosted.org/packages/2a/a0/036397bdbee0f3bd46c2e26fdfbb1a61b2140bf9059240c37b61149047fa/coverage-7.11.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c6681add5060c2742dafcf29826dff1ff8eef889a3b03390daeed84361c428bd", size = 258381, upload-time = "2025-11-07T10:52:21.687Z" }, + { url = "https://files.pythonhosted.org/packages/b6/61/2533926eb8990f182eb287f4873216c8ca530cc47241144aabf46fe80abe/coverage-7.11.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:773419b225ec9a75caa1e941dd0c83a91b92c2b525269e44e6ee3e4c630607db", size = 261321, upload-time = "2025-11-07T10:52:23.612Z" }, + { url = "https://files.pythonhosted.org/packages/32/6e/618f7e203a998e4f6b8a0fa395744a416ad2adbcdc3735bc19466456718a/coverage-7.11.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a9cb272a0e0157dbb9b2fd0b201b759bd378a1a6138a16536c025c2ce4f7643b", size = 258933, upload-time = "2025-11-07T10:52:25.514Z" }, + { url = "https://files.pythonhosted.org/packages/22/40/6b1c27f772cb08a14a338647ead1254a57ee9dabbb4cacbc15df7f278741/coverage-7.11.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:e09adb2a7811dc75998eef68f47599cf699e2b62eed09c9fefaeb290b3920f34", size = 257756, upload-time = "2025-11-07T10:52:27.845Z" }, + { url = "https://files.pythonhosted.org/packages/73/07/f9cd12f71307a785ea15b009c8d8cc2543e4a867bd04b8673843970b6b43/coverage-7.11.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1335fa8c2a2fea49924d97e1e3500cfe8d7c849f5369f26bb7559ad4259ccfab", size = 260086, upload-time = "2025-11-07T10:52:29.776Z" }, + { url = "https://files.pythonhosted.org/packages/34/02/31c5394f6f5d72a466966bcfdb61ce5a19862d452816d6ffcbb44add16ee/coverage-7.11.1-cp314-cp314t-win32.whl", hash = "sha256:4782d71d2a4fa7cef95e853b7097c8bbead4dbd0e6f9c7152a6b11a194b794db", size = 220483, upload-time = "2025-11-07T10:52:31.752Z" }, + { url = "https://files.pythonhosted.org/packages/7f/96/81e1ef5fbfd5090113a96e823dbe055e4c58d96ca73b1fb0ad9d26f9ec36/coverage-7.11.1-cp314-cp314t-win_amd64.whl", hash = "sha256:939f45e66eceb63c75e8eb8fc58bb7077c00f1a41b0e15c6ef02334a933cfe93", size = 221592, upload-time = "2025-11-07T10:52:33.724Z" }, + { url = "https://files.pythonhosted.org/packages/38/7a/a5d050de44951ac453a2046a0f3fb5471a4a557f0c914d00db27d543d94c/coverage-7.11.1-cp314-cp314t-win_arm64.whl", hash = "sha256:01c575bdbef35e3f023b50a146e9a75c53816e4f2569109458155cd2315f87d9", size = 219627, upload-time = "2025-11-07T10:52:36.285Z" }, + { url = "https://files.pythonhosted.org/packages/76/32/bd9f48c28e23b2f08946f8e83983617b00619f5538dbd7e1045fa7e88c00/coverage-7.11.1-py3-none-any.whl", hash = "sha256:0fa848acb5f1da24765cee840e1afe9232ac98a8f9431c6112c15b34e880b9e8", size = 208689, upload-time = "2025-11-07T10:52:38.646Z" }, ] [[package]] @@ -462,77 +462,77 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004 }, - { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667 }, - { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807 }, - { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615 }, - { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800 }, - { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707 }, - { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541 }, - { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464 }, - { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838 }, - { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596 }, - { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782 }, - { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381 }, - { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988 }, - { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451 }, - { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007 }, - { url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012 }, - { url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728 }, - { url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078 }, - { url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460 }, - { url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237 }, - { url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344 }, - { url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564 }, - { url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415 }, - { url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457 }, - { url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074 }, - { url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569 }, - { url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941 }, - { url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339 }, - { url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315 }, - { url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331 }, - { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248 }, - { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089 }, - { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029 }, - { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222 }, - { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280 }, - { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958 }, - { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714 }, - { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970 }, - { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236 }, - { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642 }, - { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126 }, - { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573 }, - { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695 }, - { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720 }, - { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740 }, +sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" }, + { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" }, + { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" }, + { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" }, + { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" }, + { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" }, + { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" }, + { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" }, + { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" }, + { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" }, + { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" }, + { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" }, + { url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" }, + { url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" }, + { url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" }, + { url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" }, + { url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" }, + { url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" }, + { url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" }, + { url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" }, + { url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" }, + { url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" }, + { url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" }, + { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" }, + { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" }, + { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" }, + { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" }, + { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" }, + { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" }, + { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" }, + { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" }, + { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" }, + { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" }, + { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" }, ] [[package]] name = "dnspython" version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251 } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094 }, + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] [[package]] name = "docopt" version = "0.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } [[package]] name = "dotmap" version = "1.3.30" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/68/c186606e4f2bf731abd18044ea201e70c3c244bf468f41368820d197fca5/dotmap-1.3.30.tar.gz", hash = "sha256:5821a7933f075fb47563417c0e92e0b7c031158b4c9a6a7e56163479b658b368", size = 12391 } +sdist = { url = "https://files.pythonhosted.org/packages/bc/68/c186606e4f2bf731abd18044ea201e70c3c244bf468f41368820d197fca5/dotmap-1.3.30.tar.gz", hash = "sha256:5821a7933f075fb47563417c0e92e0b7c031158b4c9a6a7e56163479b658b368", size = 12391, upload-time = "2022-04-06T16:26:49.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/f9/976d6813c160d6c89196d81e9466dca1503d20e609d8751f3536daf37ec6/dotmap-1.3.30-py3-none-any.whl", hash = "sha256:bd9fa15286ea2ad899a4d1dc2445ed85a1ae884a42effb87c89a6ecce71243c6", size = 11464 }, + { url = "https://files.pythonhosted.org/packages/4d/f9/976d6813c160d6c89196d81e9466dca1503d20e609d8751f3536daf37ec6/dotmap-1.3.30-py3-none-any.whl", hash = "sha256:bd9fa15286ea2ad899a4d1dc2445ed85a1ae884a42effb87c89a6ecce71243c6", size = 11464, upload-time = "2022-04-06T16:26:47.103Z" }, ] [[package]] @@ -543,18 +543,18 @@ dependencies = [ { name = "dnspython" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604 }, + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, ] [[package]] name = "et-xmlfile" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234 } +sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059 }, + { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, ] [[package]] @@ -564,9 +564,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "faker" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/98/75cacae9945f67cfe323829fc2ac451f64517a8a330b572a06a323997065/factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03", size = 164146 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/98/75cacae9945f67cfe323829fc2ac451f64517a8a330b572a06a323997065/factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03", size = 164146, upload-time = "2025-02-03T09:49:04.433Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/8d/2bc5f5546ff2ccb3f7de06742853483ab75bf74f36a92254702f8baecc79/factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc", size = 37036 }, + { url = "https://files.pythonhosted.org/packages/27/8d/2bc5f5546ff2ccb3f7de06742853483ab75bf74f36a92254702f8baecc79/factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc", size = 37036, upload-time = "2025-02-03T09:49:01.659Z" }, ] [[package]] @@ -576,9 +576,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/84/e95acaa848b855e15c83331d0401ee5f84b2f60889255c2e055cb4fb6bdf/faker-37.12.0.tar.gz", hash = "sha256:7505e59a7e02fa9010f06c3e1e92f8250d4cfbb30632296140c2d6dbef09b0fa", size = 1935741 } +sdist = { url = "https://files.pythonhosted.org/packages/3d/84/e95acaa848b855e15c83331d0401ee5f84b2f60889255c2e055cb4fb6bdf/faker-37.12.0.tar.gz", hash = "sha256:7505e59a7e02fa9010f06c3e1e92f8250d4cfbb30632296140c2d6dbef09b0fa", size = 1935741, upload-time = "2025-10-24T15:19:58.764Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/98/2c050dec90e295a524c9b65c4cb9e7c302386a296b2938710448cbd267d5/faker-37.12.0-py3-none-any.whl", hash = "sha256:afe7ccc038da92f2fbae30d8e16d19d91e92e242f8401ce9caf44de892bab4c4", size = 1975461 }, + { url = "https://files.pythonhosted.org/packages/8e/98/2c050dec90e295a524c9b65c4cb9e7c302386a296b2938710448cbd267d5/faker-37.12.0-py3-none-any.whl", hash = "sha256:afe7ccc038da92f2fbae30d8e16d19d91e92e242f8401ce9caf44de892bab4c4", size = 1975461, upload-time = "2025-10-24T15:19:55.739Z" }, ] [[package]] @@ -591,9 +591,9 @@ dependencies = [ { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/e3/77a2df0946703973b9905fd0cde6172c15e0781984320123b4f5079e7113/fastapi-0.121.0.tar.gz", hash = "sha256:06663356a0b1ee93e875bbf05a31fb22314f5bed455afaaad2b2dad7f26e98fa", size = 342412 } +sdist = { url = "https://files.pythonhosted.org/packages/8c/e3/77a2df0946703973b9905fd0cde6172c15e0781984320123b4f5079e7113/fastapi-0.121.0.tar.gz", hash = "sha256:06663356a0b1ee93e875bbf05a31fb22314f5bed455afaaad2b2dad7f26e98fa", size = 342412, upload-time = "2025-11-03T10:25:54.818Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/2c/42277afc1ba1a18f8358561eee40785d27becab8f80a1f945c0a3051c6eb/fastapi-0.121.0-py3-none-any.whl", hash = "sha256:8bdf1b15a55f4e4b0d6201033da9109ea15632cb76cf156e7b8b4019f2172106", size = 109183 }, + { url = "https://files.pythonhosted.org/packages/dd/2c/42277afc1ba1a18f8358561eee40785d27becab8f80a1f945c0a3051c6eb/fastapi-0.121.0-py3-none-any.whl", hash = "sha256:8bdf1b15a55f4e4b0d6201033da9109ea15632cb76cf156e7b8b4019f2172106", size = 109183, upload-time = "2025-11-03T10:25:53.27Z" }, ] [package.optional-dependencies] @@ -615,9 +615,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/13/11e43d630be84e51ba5510a6da6a11eb93b44b72caa796137c5dddda937b/fastapi_cli-0.0.14.tar.gz", hash = "sha256:ddfb5de0a67f77a8b3271af1460489bd4d7f4add73d11fbfac613827b0275274", size = 17994 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/13/11e43d630be84e51ba5510a6da6a11eb93b44b72caa796137c5dddda937b/fastapi_cli-0.0.14.tar.gz", hash = "sha256:ddfb5de0a67f77a8b3271af1460489bd4d7f4add73d11fbfac613827b0275274", size = 17994, upload-time = "2025-10-20T16:33:21.054Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/e8/bc8bbfd93dcc8e347ce98a3e654fb0d2e5f2739afb46b98f41a30c339269/fastapi_cli-0.0.14-py3-none-any.whl", hash = "sha256:e66b9ad499ee77a4e6007545cde6de1459b7f21df199d7f29aad2adaab168eca", size = 11151 }, + { url = "https://files.pythonhosted.org/packages/40/e8/bc8bbfd93dcc8e347ce98a3e654fb0d2e5f2739afb46b98f41a30c339269/fastapi_cli-0.0.14-py3-none-any.whl", hash = "sha256:e66b9ad499ee77a4e6007545cde6de1459b7f21df199d7f29aad2adaab168eca", size = 11151, upload-time = "2025-10-20T16:33:19.318Z" }, ] [package.optional-dependencies] @@ -639,9 +639,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f9/48/0f14d8555b750dc8c04382804e4214f1d7f55298127f3a0237ba566e69dd/fastapi_cloud_cli-0.3.1.tar.gz", hash = "sha256:8c7226c36e92e92d0c89827e8f56dbf164ab2de4444bd33aa26b6c3f7675db69", size = 24080 } +sdist = { url = "https://files.pythonhosted.org/packages/f9/48/0f14d8555b750dc8c04382804e4214f1d7f55298127f3a0237ba566e69dd/fastapi_cloud_cli-0.3.1.tar.gz", hash = "sha256:8c7226c36e92e92d0c89827e8f56dbf164ab2de4444bd33aa26b6c3f7675db69", size = 24080, upload-time = "2025-10-09T11:32:58.174Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/79/7f5a5e5513e6a737e5fb089d9c59c74d4d24dc24d581d3aa519b326bedda/fastapi_cloud_cli-0.3.1-py3-none-any.whl", hash = "sha256:7d1a98a77791a9d0757886b2ffbf11bcc6b3be93210dd15064be10b216bf7e00", size = 19711 }, + { url = "https://files.pythonhosted.org/packages/68/79/7f5a5e5513e6a737e5fb089d9c59c74d4d24dc24d581d3aa519b326bedda/fastapi_cloud_cli-0.3.1-py3-none-any.whl", hash = "sha256:7d1a98a77791a9d0757886b2ffbf11bcc6b3be93210dd15064be10b216bf7e00", size = 19711, upload-time = "2025-10-09T11:32:57.118Z" }, ] [[package]] @@ -652,15 +652,15 @@ dependencies = [ { name = "fastapi" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/ed/c36cfcd849519fd2d23051ad81a91fc5e8cfa7109496fc8a10ad565a5fe9/fastapi_filter-2.0.1.tar.gz", hash = "sha256:cffda370097af7e404f1eb188aca58b199084bfaf7cec881e40b404adf12566e", size = 9857 } +sdist = { url = "https://files.pythonhosted.org/packages/43/ed/c36cfcd849519fd2d23051ad81a91fc5e8cfa7109496fc8a10ad565a5fe9/fastapi_filter-2.0.1.tar.gz", hash = "sha256:cffda370097af7e404f1eb188aca58b199084bfaf7cec881e40b404adf12566e", size = 9857, upload-time = "2024-12-07T17:30:06.343Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/88/afc022ad64d12f730141fc50758ecf9d60de5fed11335dc16e3127617f05/fastapi_filter-2.0.1-py3-none-any.whl", hash = "sha256:711d48707ec62f7c9e12a7713fc0f6a99858a9e3741b4d108102d5599e77197d", size = 11586 }, + { url = "https://files.pythonhosted.org/packages/5e/88/afc022ad64d12f730141fc50758ecf9d60de5fed11335dc16e3127617f05/fastapi_filter-2.0.1-py3-none-any.whl", hash = "sha256:711d48707ec62f7c9e12a7713fc0f6a99858a9e3741b4d108102d5599e77197d", size = 11586, upload-time = "2024-12-07T17:30:05.375Z" }, ] [[package]] name = "fastapi-mail" version = "1.2.6" -source = { git = "https://github.com/simonvanlierde/fastapi-mail?rev=f32147ec1a450ed22262913c5ac7ec3b67dd0117#f32147ec1a450ed22262913c5ac7ec3b67dd0117" } +source = { git = "https://github.com/simonvanlierde/fastapi-mail?rev=6c6f04a7afaf3cdced82764009a2f1f2a3c3ee6c#6c6f04a7afaf3cdced82764009a2f1f2a3c3ee6c" } dependencies = [ { name = "aiosmtplib" }, { name = "blinker" }, @@ -683,9 +683,9 @@ dependencies = [ { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bc/db/8a3d097c491ad873574bd7295834ef89e16263ae9104855bbb5ee6d46e47/fastapi_pagination-0.15.0.tar.gz", hash = "sha256:11fe39cbe181ed3c18919b90faf6bfcbe40cb596aa9c52a98bbce85111a29a4f", size = 557472 } +sdist = { url = "https://files.pythonhosted.org/packages/bc/db/8a3d097c491ad873574bd7295834ef89e16263ae9104855bbb5ee6d46e47/fastapi_pagination-0.15.0.tar.gz", hash = "sha256:11fe39cbe181ed3c18919b90faf6bfcbe40cb596aa9c52a98bbce85111a29a4f", size = 557472, upload-time = "2025-10-28T19:06:17.732Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/91/b835e07234170ba85473227aa107bcf1dc616ff6cb643c0bd9b8225a55f1/fastapi_pagination-0.15.0-py3-none-any.whl", hash = "sha256:ffef937e78903fcb6f356b8407ec1fb0620a06675087fa7d0c4e537a60aa0447", size = 52292 }, + { url = "https://files.pythonhosted.org/packages/68/91/b835e07234170ba85473227aa107bcf1dc616ff6cb643c0bd9b8225a55f1/fastapi_pagination-0.15.0-py3-none-any.whl", hash = "sha256:ffef937e78903fcb6f356b8407ec1fb0620a06675087fa7d0c4e537a60aa0447", size = 52292, upload-time = "2025-10-28T19:06:16.371Z" }, ] [[package]] @@ -695,9 +695,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "boto3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/8a/e56d4ade659994c2989091b96642f10554ec3914a40de56a556ffdbbcd26/fastapi_storages-0.3.0.tar.gz", hash = "sha256:f784335fff9cd163b783e842da04c6d9ed1b306fce8995fda109b170d6d453df", size = 6706 } +sdist = { url = "https://files.pythonhosted.org/packages/68/8a/e56d4ade659994c2989091b96642f10554ec3914a40de56a556ffdbbcd26/fastapi_storages-0.3.0.tar.gz", hash = "sha256:f784335fff9cd163b783e842da04c6d9ed1b306fce8995fda109b170d6d453df", size = 6706, upload-time = "2024-02-15T15:14:26.431Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/b5/3fb94b4f329fb2f83ffa54e6941b769deaaaa77f63a9fe70905219dd7339/fastapi_storages-0.3.0-py3-none-any.whl", hash = "sha256:91adb41a80fdef2a84c0f8244c27ade7ff8bd5db9b7fa95c496c06c03e192477", size = 9725 }, + { url = "https://files.pythonhosted.org/packages/a7/b5/3fb94b4f329fb2f83ffa54e6941b769deaaaa77f63a9fe70905219dd7339/fastapi_storages-0.3.0-py3-none-any.whl", hash = "sha256:91adb41a80fdef2a84c0f8244c27ade7ff8bd5db9b7fa95c496c06c03e192477", size = 9725, upload-time = "2024-02-15T15:14:27.567Z" }, ] [[package]] @@ -712,9 +712,9 @@ dependencies = [ { name = "pyjwt", extra = ["crypto"] }, { name = "python-multipart" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/ea/6c0ba809f29d22ad53ab25bbae4408f00b0a3375b71bd21c39dcc3a16044/fastapi_users-15.0.1.tar.gz", hash = "sha256:c822755c1288740a919636d3463797e54df91b53c1c6f4917693d499867d21a7", size = 120916 } +sdist = { url = "https://files.pythonhosted.org/packages/fa/ea/6c0ba809f29d22ad53ab25bbae4408f00b0a3375b71bd21c39dcc3a16044/fastapi_users-15.0.1.tar.gz", hash = "sha256:c822755c1288740a919636d3463797e54df91b53c1c6f4917693d499867d21a7", size = 120916, upload-time = "2025-10-25T06:52:45.735Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/7f/1bff91a48e755e659d0505f597a8e010ec92059f2219a838fd15887a89b2/fastapi_users-15.0.1-py3-none-any.whl", hash = "sha256:6f637eb2fc80be6bba396b77dded30fe4c22fa943349d2e0a1647894f8b21c16", size = 38624 }, + { url = "https://files.pythonhosted.org/packages/59/7f/1bff91a48e755e659d0505f597a8e010ec92059f2219a838fd15887a89b2/fastapi_users-15.0.1-py3-none-any.whl", hash = "sha256:6f637eb2fc80be6bba396b77dded30fe4c22fa943349d2e0a1647894f8b21c16", size = 38624, upload-time = "2025-10-25T06:52:44.119Z" }, ] [package.optional-dependencies] @@ -733,9 +733,9 @@ dependencies = [ { name = "fastapi-users" }, { name = "sqlalchemy", extra = ["asyncio"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/87/12/bc9e6146ae31564741cefc87ee6e37fa5b566933f0afe8aa030779d60e60/fastapi_users_db_sqlalchemy-7.0.0.tar.gz", hash = "sha256:6823eeedf8a92f819276a2b2210ef1dcfd71fe8b6e37f7b4da8d1c60e3dfd595", size = 10877 } +sdist = { url = "https://files.pythonhosted.org/packages/87/12/bc9e6146ae31564741cefc87ee6e37fa5b566933f0afe8aa030779d60e60/fastapi_users_db_sqlalchemy-7.0.0.tar.gz", hash = "sha256:6823eeedf8a92f819276a2b2210ef1dcfd71fe8b6e37f7b4da8d1c60e3dfd595", size = 10877, upload-time = "2025-01-04T13:09:05.086Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/08/9968963c1fb8c34627b7f1fbcdfe9438540f87dc7c9bfb59bb4fd19a4ecf/fastapi_users_db_sqlalchemy-7.0.0-py3-none-any.whl", hash = "sha256:5fceac018e7cfa69efc70834dd3035b3de7988eb4274154a0dbe8b14f5aa001e", size = 6891 }, + { url = "https://files.pythonhosted.org/packages/a6/08/9968963c1fb8c34627b7f1fbcdfe9438540f87dc7c9bfb59bb4fd19a4ecf/fastapi_users_db_sqlalchemy-7.0.0-py3-none-any.whl", hash = "sha256:5fceac018e7cfa69efc70834dd3035b3de7988eb4274154a0dbe8b14f5aa001e", size = 6891, upload-time = "2025-01-04T13:09:02.869Z" }, ] [[package]] @@ -752,9 +752,9 @@ dependencies = [ name = "filelock" version = "3.20.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922 } +sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054 }, + { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" }, ] [[package]] @@ -768,9 +768,9 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/da/83d7043169ac2c8c7469f0e375610d78ae2160134bf1b80634c482fa079c/google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8", size = 176759 } +sdist = { url = "https://files.pythonhosted.org/packages/61/da/83d7043169ac2c8c7469f0e375610d78ae2160134bf1b80634c482fa079c/google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8", size = 176759, upload-time = "2025-10-28T21:34:51.529Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/d4/90197b416cb61cefd316964fd9e7bd8324bcbafabf40eef14a9f20b81974/google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c", size = 173706 }, + { url = "https://files.pythonhosted.org/packages/ed/d4/90197b416cb61cefd316964fd9e7bd8324bcbafabf40eef14a9f20b81974/google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c", size = 173706, upload-time = "2025-10-28T21:34:50.151Z" }, ] [[package]] @@ -784,9 +784,9 @@ dependencies = [ { name = "httplib2" }, { name = "uritemplate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/83/60cdacf139d768dd7f0fcbe8d95b418299810068093fdf8228c6af89bb70/google_api_python_client-2.187.0.tar.gz", hash = "sha256:e98e8e8f49e1b5048c2f8276473d6485febc76c9c47892a8b4d1afa2c9ec8278", size = 14068154 } +sdist = { url = "https://files.pythonhosted.org/packages/75/83/60cdacf139d768dd7f0fcbe8d95b418299810068093fdf8228c6af89bb70/google_api_python_client-2.187.0.tar.gz", hash = "sha256:e98e8e8f49e1b5048c2f8276473d6485febc76c9c47892a8b4d1afa2c9ec8278", size = 14068154, upload-time = "2025-11-06T01:48:53.274Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/58/c1e716be1b055b504d80db2c8413f6c6a890a6ae218a65f178b63bc30356/google_api_python_client-2.187.0-py3-none-any.whl", hash = "sha256:d8d0f6d85d7d1d10bdab32e642312ed572bdc98919f72f831b44b9a9cebba32f", size = 14641434 }, + { url = "https://files.pythonhosted.org/packages/96/58/c1e716be1b055b504d80db2c8413f6c6a890a6ae218a65f178b63bc30356/google_api_python_client-2.187.0-py3-none-any.whl", hash = "sha256:d8d0f6d85d7d1d10bdab32e642312ed572bdc98919f72f831b44b9a9cebba32f", size = 14641434, upload-time = "2025-11-06T01:48:50.763Z" }, ] [[package]] @@ -798,9 +798,9 @@ dependencies = [ { name = "pyasn1-modules" }, { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/ef/66d14cf0e01b08d2d51ffc3c20410c4e134a1548fc246a6081eae585a4fe/google_auth-2.43.0.tar.gz", hash = "sha256:88228eee5fc21b62a1b5fe773ca15e67778cb07dc8363adcb4a8827b52d81483", size = 296359 } +sdist = { url = "https://files.pythonhosted.org/packages/ff/ef/66d14cf0e01b08d2d51ffc3c20410c4e134a1548fc246a6081eae585a4fe/google_auth-2.43.0.tar.gz", hash = "sha256:88228eee5fc21b62a1b5fe773ca15e67778cb07dc8363adcb4a8827b52d81483", size = 296359, upload-time = "2025-11-06T00:13:36.587Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/d1/385110a9ae86d91cc14c5282c61fe9f4dc41c0b9f7d423c6ad77038c4448/google_auth-2.43.0-py2.py3-none-any.whl", hash = "sha256:af628ba6fa493f75c7e9dbe9373d148ca9f4399b5ea29976519e0a3848eddd16", size = 223114 }, + { url = "https://files.pythonhosted.org/packages/6f/d1/385110a9ae86d91cc14c5282c61fe9f4dc41c0b9f7d423c6ad77038c4448/google_auth-2.43.0-py2.py3-none-any.whl", hash = "sha256:af628ba6fa493f75c7e9dbe9373d148ca9f4399b5ea29976519e0a3848eddd16", size = 223114, upload-time = "2025-11-06T00:13:35.209Z" }, ] [[package]] @@ -811,9 +811,9 @@ dependencies = [ { name = "google-auth" }, { name = "httplib2" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e0/83/7ef576d1c7ccea214e7b001e69c006bc75e058a3a1f2ab810167204b698b/google_auth_httplib2-0.2.1.tar.gz", hash = "sha256:5ef03be3927423c87fb69607b42df23a444e434ddb2555b73b3679793187b7de", size = 11086 } +sdist = { url = "https://files.pythonhosted.org/packages/e0/83/7ef576d1c7ccea214e7b001e69c006bc75e058a3a1f2ab810167204b698b/google_auth_httplib2-0.2.1.tar.gz", hash = "sha256:5ef03be3927423c87fb69607b42df23a444e434ddb2555b73b3679793187b7de", size = 11086, upload-time = "2025-10-30T21:13:16.569Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/a7/ca23dd006255f70e2bc469d3f9f0c82ea455335bfd682ad4d677adc435de/google_auth_httplib2-0.2.1-py3-none-any.whl", hash = "sha256:1be94c611db91c01f9703e7f62b0a59bbd5587a95571c7b6fade510d648bc08b", size = 9525 }, + { url = "https://files.pythonhosted.org/packages/44/a7/ca23dd006255f70e2bc469d3f9f0c82ea455335bfd682ad4d677adc435de/google_auth_httplib2-0.2.1-py3-none-any.whl", hash = "sha256:1be94c611db91c01f9703e7f62b0a59bbd5587a95571c7b6fade510d648bc08b", size = 9525, upload-time = "2025-10-30T21:13:15.758Z" }, ] [[package]] @@ -823,46 +823,46 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433 } +sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433, upload-time = "2025-11-06T18:29:24.087Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515 }, + { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515, upload-time = "2025-11-06T18:29:13.14Z" }, ] [[package]] name = "greenlet" version = "3.2.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814 }, - { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073 }, - { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191 }, - { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516 }, - { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169 }, - { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497 }, - { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662 }, - { url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210 }, - { url = "https://files.pythonhosted.org/packages/1c/53/f9c440463b3057485b8594d7a638bed53ba531165ef0ca0e6c364b5cc807/greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b", size = 1564759 }, - { url = "https://files.pythonhosted.org/packages/47/e4/3bb4240abdd0a8d23f4f88adec746a3099f0d86bfedb623f063b2e3b4df0/greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929", size = 1634288 }, - { url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685 }, - { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586 }, - { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346 }, - { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218 }, - { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659 }, - { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355 }, - { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512 }, - { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508 }, - { url = "https://files.pythonhosted.org/packages/0d/da/343cd760ab2f92bac1845ca07ee3faea9fe52bee65f7bcb19f16ad7de08b/greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681", size = 1680760 }, - { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425 }, +sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" }, + { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191, upload-time = "2025-08-07T13:45:29.752Z" }, + { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516, upload-time = "2025-08-07T13:53:16.314Z" }, + { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169, upload-time = "2025-08-07T13:18:32.861Z" }, + { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" }, + { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" }, + { url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210, upload-time = "2025-08-07T13:18:24.072Z" }, + { url = "https://files.pythonhosted.org/packages/1c/53/f9c440463b3057485b8594d7a638bed53ba531165ef0ca0e6c364b5cc807/greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b", size = 1564759, upload-time = "2025-11-04T12:42:19.395Z" }, + { url = "https://files.pythonhosted.org/packages/47/e4/3bb4240abdd0a8d23f4f88adec746a3099f0d86bfedb623f063b2e3b4df0/greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929", size = 1634288, upload-time = "2025-11-04T12:42:21.174Z" }, + { url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685, upload-time = "2025-08-07T13:24:38.824Z" }, + { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" }, + { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" }, + { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218, upload-time = "2025-08-07T13:45:30.969Z" }, + { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" }, + { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" }, + { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" }, + { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508, upload-time = "2025-11-04T12:42:23.427Z" }, + { url = "https://files.pythonhosted.org/packages/0d/da/343cd760ab2f92bac1845ca07ee3faea9fe52bee65f7bcb19f16ad7de08b/greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681", size = 1680760, upload-time = "2025-11-04T12:42:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425, upload-time = "2025-08-07T13:32:27.59Z" }, ] [[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 } +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 }, + { 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]] @@ -873,9 +873,9 @@ 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 } +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 }, + { 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]] @@ -885,31 +885,31 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyparsing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/77/6653db69c1f7ecfe5e3f9726fdadc981794656fcd7d98c4209fecfea9993/httplib2-0.31.0.tar.gz", hash = "sha256:ac7ab497c50975147d4f7b1ade44becc7df2f8954d42b38b3d69c515f531135c", size = 250759 } +sdist = { url = "https://files.pythonhosted.org/packages/52/77/6653db69c1f7ecfe5e3f9726fdadc981794656fcd7d98c4209fecfea9993/httplib2-0.31.0.tar.gz", hash = "sha256:ac7ab497c50975147d4f7b1ade44becc7df2f8954d42b38b3d69c515f531135c", size = 250759, upload-time = "2025-09-11T12:16:03.403Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/a2/0d269db0f6163be503775dc8b6a6fa15820cc9fdc866f6ba608d86b721f2/httplib2-0.31.0-py3-none-any.whl", hash = "sha256:b9cd78abea9b4e43a7714c6e0f8b6b8561a6fc1e95d5dbd367f5bf0ef35f5d24", size = 91148 }, + { url = "https://files.pythonhosted.org/packages/8c/a2/0d269db0f6163be503775dc8b6a6fa15820cc9fdc866f6ba608d86b721f2/httplib2-0.31.0-py3-none-any.whl", hash = "sha256:b9cd78abea9b4e43a7714c6e0f8b6b8561a6fc1e95d5dbd367f5bf0ef35f5d24", size = 91148, upload-time = "2025-09-11T12:16:01.803Z" }, ] [[package]] name = "httptools" version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889 }, - { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180 }, - { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596 }, - { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268 }, - { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517 }, - { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337 }, - { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743 }, - { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619 }, - { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714 }, - { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909 }, - { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831 }, - { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631 }, - { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910 }, - { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205 }, + { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" }, + { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" }, + { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" }, + { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" }, + { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" }, + { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" }, + { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" }, + { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" }, + { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" }, + { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" }, + { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" }, + { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" }, + { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" }, + { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" }, ] [[package]] @@ -922,9 +922,9 @@ dependencies = [ { 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 } +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 }, + { 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]] @@ -934,9 +934,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/07/db4ad128da3926be22eec586aa87dafd8840c9eb03fe88505fbed016b5c6/httpx_oauth-0.16.1.tar.gz", hash = "sha256:7402f061f860abc092ea4f5c90acfc576a40bbb79633c1d2920f1ca282c296ee", size = 44148 } +sdist = { url = "https://files.pythonhosted.org/packages/2f/07/db4ad128da3926be22eec586aa87dafd8840c9eb03fe88505fbed016b5c6/httpx_oauth-0.16.1.tar.gz", hash = "sha256:7402f061f860abc092ea4f5c90acfc576a40bbb79633c1d2920f1ca282c296ee", size = 44148, upload-time = "2024-12-20T07:23:02.589Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/4b/2b81e876abf77b4af3372aff731f4f6722840ebc7dcfd85778eaba271733/httpx_oauth-0.16.1-py3-none-any.whl", hash = "sha256:2fcad82f80f28d0473a0fc4b4eda223dc952050af7e3a8c8781342d850f09fb5", size = 38056 }, + { url = "https://files.pythonhosted.org/packages/45/4b/2b81e876abf77b4af3372aff731f4f6722840ebc7dcfd85778eaba271733/httpx_oauth-0.16.1-py3-none-any.whl", hash = "sha256:2fcad82f80f28d0473a0fc4b4eda223dc952050af7e3a8c8781342d850f09fb5", size = 38056, upload-time = "2024-12-20T07:23:00.394Z" }, ] [[package]] @@ -946,36 +946,36 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyreadline3", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794 }, + { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" }, ] [[package]] name = "idna" version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] [[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 } +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 }, + { 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 = "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 } +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 }, + { 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]] @@ -985,27 +985,27 @@ 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 } +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 }, + { 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.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843 } +sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843, upload-time = "2022-06-17T18:00:12.224Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256 }, + { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" }, ] [[package]] name = "makefun" version = "1.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/cf/6780ab8bc3b84a1cce3e4400aed3d64b6db7d5e227a2f75b6ded5674701a/makefun-1.16.0.tar.gz", hash = "sha256:e14601831570bff1f6d7e68828bcd30d2f5856f24bad5de0ccb22921ceebc947", size = 73565 } +sdist = { url = "https://files.pythonhosted.org/packages/7b/cf/6780ab8bc3b84a1cce3e4400aed3d64b6db7d5e227a2f75b6ded5674701a/makefun-1.16.0.tar.gz", hash = "sha256:e14601831570bff1f6d7e68828bcd30d2f5856f24bad5de0ccb22921ceebc947", size = 73565, upload-time = "2025-05-09T15:00:42.313Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/c0/4bc973defd1270b89ccaae04cef0d5fa3ea85b59b108ad2c08aeea9afb76/makefun-1.16.0-py2.py3-none-any.whl", hash = "sha256:43baa4c3e7ae2b17de9ceac20b669e9a67ceeadff31581007cca20a07bbe42c4", size = 22923 }, + { url = "https://files.pythonhosted.org/packages/b7/c0/4bc973defd1270b89ccaae04cef0d5fa3ea85b59b108ad2c08aeea9afb76/makefun-1.16.0-py2.py3-none-any.whl", hash = "sha256:43baa4c3e7ae2b17de9ceac20b669e9a67ceeadff31581007cca20a07bbe42c4", size = 22923, upload-time = "2025-05-09T15:00:41.042Z" }, ] [[package]] @@ -1015,18 +1015,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474 } +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509 }, + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, ] [[package]] name = "markdown" version = "3.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/7dd27d9d863b3376fcf23a5a13cb5d024aed1db46f963f1b5735ae43b3be/markdown-3.10.tar.gz", hash = "sha256:37062d4f2aa4b2b6b32aefb80faa300f82cc790cb949a35b8caede34f2b68c0e", size = 364931 } +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/7dd27d9d863b3376fcf23a5a13cb5d024aed1db46f963f1b5735ae43b3be/markdown-3.10.tar.gz", hash = "sha256:37062d4f2aa4b2b6b32aefb80faa300f82cc790cb949a35b8caede34f2b68c0e", size = 364931, upload-time = "2025-11-03T19:51:15.007Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl", hash = "sha256:b5b99d6951e2e4948d939255596523444c0e677c669700b1d17aa4a8a464cb7c", size = 107678 }, + { url = "https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl", hash = "sha256:b5b99d6951e2e4948d939255596523444c0e677c669700b1d17aa4a8a464cb7c", size = 107678, upload-time = "2025-11-03T19:51:13.887Z" }, ] [[package]] @@ -1036,70 +1036,70 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070 } +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321 }, + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, ] [[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 } -wheels = [ - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 }, - { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 }, - { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 }, - { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 }, - { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 }, - { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 }, - { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 }, - { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 }, - { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 }, +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/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 = "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 } +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 }, + { 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]] @@ -1112,70 +1112,70 @@ dependencies = [ { name = "dotmap" }, { name = "jinja2" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/68/4e0e1b0bc64f0d3afac2fb8a4fb35f2a4e9a0521ae1c777c0e29e21b27fa/mjml-0.11.1.tar.gz", hash = "sha256:f703c8b3458ca0100df6cf56a3633f193b352a80b1a1836a452b92361e74ca73", size = 66589 } +sdist = { url = "https://files.pythonhosted.org/packages/2a/68/4e0e1b0bc64f0d3afac2fb8a4fb35f2a4e9a0521ae1c777c0e29e21b27fa/mjml-0.11.1.tar.gz", hash = "sha256:f703c8b3458ca0100df6cf56a3633f193b352a80b1a1836a452b92361e74ca73", size = 66589, upload-time = "2025-05-13T10:24:05.693Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/a6/7ed27888adbf8cbdd734e298691004918ec0ef5f40e6bc1329ed97da2273/mjml-0.11.1-py3-none-any.whl", hash = "sha256:fef9f7a95929cbe5ddce9351ee8702e05153d68abc77dcf8e84da2c22a330b2a", size = 63191 }, + { url = "https://files.pythonhosted.org/packages/85/a6/7ed27888adbf8cbdd734e298691004918ec0ef5f40e6bc1329ed97da2273/mjml-0.11.1-py3-none-any.whl", hash = "sha256:fef9f7a95929cbe5ddce9351ee8702e05153d68abc77dcf8e84da2c22a330b2a", size = 63191, upload-time = "2025-05-13T10:24:03.953Z" }, ] [[package]] name = "nodeenv" version = "1.9.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, ] [[package]] name = "numpy" version = "2.3.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/f4/098d2270d52b41f1bd7db9fc288aaa0400cb48c2a3e2af6fa365d9720947/numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a", size = 20582187 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/7e/b72610cc91edf138bc588df5150957a4937221ca6058b825b4725c27be62/numpy-2.3.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c090d4860032b857d94144d1a9976b8e36709e40386db289aaf6672de2a81966", size = 20950335 }, - { url = "https://files.pythonhosted.org/packages/3e/46/bdd3370dcea2f95ef14af79dbf81e6927102ddf1cc54adc0024d61252fd9/numpy-2.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a13fc473b6db0be619e45f11f9e81260f7302f8d180c49a22b6e6120022596b3", size = 14179878 }, - { url = "https://files.pythonhosted.org/packages/ac/01/5a67cb785bda60f45415d09c2bc245433f1c68dd82eef9c9002c508b5a65/numpy-2.3.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:3634093d0b428e6c32c3a69b78e554f0cd20ee420dcad5a9f3b2a63762ce4197", size = 5108673 }, - { url = "https://files.pythonhosted.org/packages/c2/cd/8428e23a9fcebd33988f4cb61208fda832800ca03781f471f3727a820704/numpy-2.3.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:043885b4f7e6e232d7df4f51ffdef8c36320ee9d5f227b380ea636722c7ed12e", size = 6641438 }, - { url = "https://files.pythonhosted.org/packages/3e/d1/913fe563820f3c6b079f992458f7331278dcd7ba8427e8e745af37ddb44f/numpy-2.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7", size = 14281290 }, - { url = "https://files.pythonhosted.org/packages/9e/7e/7d306ff7cb143e6d975cfa7eb98a93e73495c4deabb7d1b5ecf09ea0fd69/numpy-2.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc8a63918b04b8571789688b2780ab2b4a33ab44bfe8ccea36d3eba51228c953", size = 16636543 }, - { url = "https://files.pythonhosted.org/packages/47/6a/8cfc486237e56ccfb0db234945552a557ca266f022d281a2f577b98e955c/numpy-2.3.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40cc556d5abbc54aabe2b1ae287042d7bdb80c08edede19f0c0afb36ae586f37", size = 16056117 }, - { url = "https://files.pythonhosted.org/packages/b1/0e/42cb5e69ea901e06ce24bfcc4b5664a56f950a70efdcf221f30d9615f3f3/numpy-2.3.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ecb63014bb7f4ce653f8be7f1df8cbc6093a5a2811211770f6606cc92b5a78fd", size = 18577788 }, - { url = "https://files.pythonhosted.org/packages/86/92/41c3d5157d3177559ef0a35da50f0cda7fa071f4ba2306dd36818591a5bc/numpy-2.3.4-cp313-cp313-win32.whl", hash = "sha256:e8370eb6925bb8c1c4264fec52b0384b44f675f191df91cbe0140ec9f0955646", size = 6282620 }, - { url = "https://files.pythonhosted.org/packages/09/97/fd421e8bc50766665ad35536c2bb4ef916533ba1fdd053a62d96cc7c8b95/numpy-2.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:56209416e81a7893036eea03abcb91c130643eb14233b2515c90dcac963fe99d", size = 12784672 }, - { url = "https://files.pythonhosted.org/packages/ad/df/5474fb2f74970ca8eb978093969b125a84cc3d30e47f82191f981f13a8a0/numpy-2.3.4-cp313-cp313-win_arm64.whl", hash = "sha256:a700a4031bc0fd6936e78a752eefb79092cecad2599ea9c8039c548bc097f9bc", size = 10196702 }, - { url = "https://files.pythonhosted.org/packages/11/83/66ac031464ec1767ea3ed48ce40f615eb441072945e98693bec0bcd056cc/numpy-2.3.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:86966db35c4040fdca64f0816a1c1dd8dbd027d90fca5a57e00e1ca4cd41b879", size = 21049003 }, - { url = "https://files.pythonhosted.org/packages/5f/99/5b14e0e686e61371659a1d5bebd04596b1d72227ce36eed121bb0aeab798/numpy-2.3.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:838f045478638b26c375ee96ea89464d38428c69170360b23a1a50fa4baa3562", size = 14302980 }, - { url = "https://files.pythonhosted.org/packages/2c/44/e9486649cd087d9fc6920e3fc3ac2aba10838d10804b1e179fb7cbc4e634/numpy-2.3.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d7315ed1dab0286adca467377c8381cd748f3dc92235f22a7dfc42745644a96a", size = 5231472 }, - { url = "https://files.pythonhosted.org/packages/3e/51/902b24fa8887e5fe2063fd61b1895a476d0bbf46811ab0c7fdf4bd127345/numpy-2.3.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:84f01a4d18b2cc4ade1814a08e5f3c907b079c847051d720fad15ce37aa930b6", size = 6739342 }, - { url = "https://files.pythonhosted.org/packages/34/f1/4de9586d05b1962acdcdb1dc4af6646361a643f8c864cef7c852bf509740/numpy-2.3.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:817e719a868f0dacde4abdfc5c1910b301877970195db9ab6a5e2c4bd5b121f7", size = 14354338 }, - { url = "https://files.pythonhosted.org/packages/1f/06/1c16103b425de7969d5a76bdf5ada0804b476fed05d5f9e17b777f1cbefd/numpy-2.3.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85e071da78d92a214212cacea81c6da557cab307f2c34b5f85b628e94803f9c0", size = 16702392 }, - { url = "https://files.pythonhosted.org/packages/34/b2/65f4dc1b89b5322093572b6e55161bb42e3e0487067af73627f795cc9d47/numpy-2.3.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2ec646892819370cf3558f518797f16597b4e4669894a2ba712caccc9da53f1f", size = 16134998 }, - { url = "https://files.pythonhosted.org/packages/d4/11/94ec578896cdb973aaf56425d6c7f2aff4186a5c00fac15ff2ec46998b46/numpy-2.3.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:035796aaaddfe2f9664b9a9372f089cfc88bd795a67bd1bfe15e6e770934cf64", size = 18651574 }, - { url = "https://files.pythonhosted.org/packages/62/b7/7efa763ab33dbccf56dade36938a77345ce8e8192d6b39e470ca25ff3cd0/numpy-2.3.4-cp313-cp313t-win32.whl", hash = "sha256:fea80f4f4cf83b54c3a051f2f727870ee51e22f0248d3114b8e755d160b38cfb", size = 6413135 }, - { url = "https://files.pythonhosted.org/packages/43/70/aba4c38e8400abcc2f345e13d972fb36c26409b3e644366db7649015f291/numpy-2.3.4-cp313-cp313t-win_amd64.whl", hash = "sha256:15eea9f306b98e0be91eb344a94c0e630689ef302e10c2ce5f7e11905c704f9c", size = 12928582 }, - { url = "https://files.pythonhosted.org/packages/67/63/871fad5f0073fc00fbbdd7232962ea1ac40eeaae2bba66c76214f7954236/numpy-2.3.4-cp313-cp313t-win_arm64.whl", hash = "sha256:b6c231c9c2fadbae4011ca5e7e83e12dc4a5072f1a1d85a0a7b3ed754d145a40", size = 10266691 }, - { url = "https://files.pythonhosted.org/packages/72/71/ae6170143c115732470ae3a2d01512870dd16e0953f8a6dc89525696069b/numpy-2.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81c3e6d8c97295a7360d367f9f8553973651b76907988bb6066376bc2252f24e", size = 20955580 }, - { url = "https://files.pythonhosted.org/packages/af/39/4be9222ffd6ca8a30eda033d5f753276a9c3426c397bb137d8e19dedd200/numpy-2.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7c26b0b2bf58009ed1f38a641f3db4be8d960a417ca96d14e5b06df1506d41ff", size = 14188056 }, - { url = "https://files.pythonhosted.org/packages/6c/3d/d85f6700d0a4aa4f9491030e1021c2b2b7421b2b38d01acd16734a2bfdc7/numpy-2.3.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:62b2198c438058a20b6704351b35a1d7db881812d8512d67a69c9de1f18ca05f", size = 5116555 }, - { url = "https://files.pythonhosted.org/packages/bf/04/82c1467d86f47eee8a19a464c92f90a9bb68ccf14a54c5224d7031241ffb/numpy-2.3.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:9d729d60f8d53a7361707f4b68a9663c968882dd4f09e0d58c044c8bf5faee7b", size = 6643581 }, - { url = "https://files.pythonhosted.org/packages/0c/d3/c79841741b837e293f48bd7db89d0ac7a4f2503b382b78a790ef1dc778a5/numpy-2.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd0c630cf256b0a7fd9d0a11c9413b42fef5101219ce6ed5a09624f5a65392c7", size = 14299186 }, - { url = "https://files.pythonhosted.org/packages/e8/7e/4a14a769741fbf237eec5a12a2cbc7a4c4e061852b6533bcb9e9a796c908/numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5e081bc082825f8b139f9e9fe42942cb4054524598aaeb177ff476cc76d09d2", size = 16638601 }, - { url = "https://files.pythonhosted.org/packages/93/87/1c1de269f002ff0a41173fe01dcc925f4ecff59264cd8f96cf3b60d12c9b/numpy-2.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15fb27364ed84114438fff8aaf998c9e19adbeba08c0b75409f8c452a8692c52", size = 16074219 }, - { url = "https://files.pythonhosted.org/packages/cd/28/18f72ee77408e40a76d691001ae599e712ca2a47ddd2c4f695b16c65f077/numpy-2.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:85d9fb2d8cd998c84d13a79a09cc0c1091648e848e4e6249b0ccd7f6b487fa26", size = 18576702 }, - { url = "https://files.pythonhosted.org/packages/c3/76/95650169b465ececa8cf4b2e8f6df255d4bf662775e797ade2025cc51ae6/numpy-2.3.4-cp314-cp314-win32.whl", hash = "sha256:e73d63fd04e3a9d6bc187f5455d81abfad05660b212c8804bf3b407e984cd2bc", size = 6337136 }, - { url = "https://files.pythonhosted.org/packages/dc/89/a231a5c43ede5d6f77ba4a91e915a87dea4aeea76560ba4d2bf185c683f0/numpy-2.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:3da3491cee49cf16157e70f607c03a217ea6647b1cea4819c4f48e53d49139b9", size = 12920542 }, - { url = "https://files.pythonhosted.org/packages/0d/0c/ae9434a888f717c5ed2ff2393b3f344f0ff6f1c793519fa0c540461dc530/numpy-2.3.4-cp314-cp314-win_arm64.whl", hash = "sha256:6d9cd732068e8288dbe2717177320723ccec4fb064123f0caf9bbd90ab5be868", size = 10480213 }, - { url = "https://files.pythonhosted.org/packages/83/4b/c4a5f0841f92536f6b9592694a5b5f68c9ab37b775ff342649eadf9055d3/numpy-2.3.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:22758999b256b595cf0b1d102b133bb61866ba5ceecf15f759623b64c020c9ec", size = 21052280 }, - { url = "https://files.pythonhosted.org/packages/3e/80/90308845fc93b984d2cc96d83e2324ce8ad1fd6efea81b324cba4b673854/numpy-2.3.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9cb177bc55b010b19798dc5497d540dea67fd13a8d9e882b2dae71de0cf09eb3", size = 14302930 }, - { url = "https://files.pythonhosted.org/packages/3d/4e/07439f22f2a3b247cec4d63a713faae55e1141a36e77fb212881f7cda3fb/numpy-2.3.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0f2bcc76f1e05e5ab58893407c63d90b2029908fa41f9f1cc51eecce936c3365", size = 5231504 }, - { url = "https://files.pythonhosted.org/packages/ab/de/1e11f2547e2fe3d00482b19721855348b94ada8359aef5d40dd57bfae9df/numpy-2.3.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dc20bde86802df2ed8397a08d793da0ad7a5fd4ea3ac85d757bf5dd4ad7c252", size = 6739405 }, - { url = "https://files.pythonhosted.org/packages/3b/40/8cd57393a26cebe2e923005db5134a946c62fa56a1087dc7c478f3e30837/numpy-2.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e199c087e2aa71c8f9ce1cb7a8e10677dc12457e7cc1be4798632da37c3e86e", size = 14354866 }, - { url = "https://files.pythonhosted.org/packages/93/39/5b3510f023f96874ee6fea2e40dfa99313a00bf3ab779f3c92978f34aace/numpy-2.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85597b2d25ddf655495e2363fe044b0ae999b75bc4d630dc0d886484b03a5eb0", size = 16703296 }, - { url = "https://files.pythonhosted.org/packages/41/0d/19bb163617c8045209c1996c4e427bccbc4bbff1e2c711f39203c8ddbb4a/numpy-2.3.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04a69abe45b49c5955923cf2c407843d1c85013b424ae8a560bba16c92fe44a0", size = 16136046 }, - { url = "https://files.pythonhosted.org/packages/e2/c1/6dba12fdf68b02a21ac411c9df19afa66bed2540f467150ca64d246b463d/numpy-2.3.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e1708fac43ef8b419c975926ce1eaf793b0c13b7356cfab6ab0dc34c0a02ac0f", size = 18652691 }, - { url = "https://files.pythonhosted.org/packages/f8/73/f85056701dbbbb910c51d846c58d29fd46b30eecd2b6ba760fc8b8a1641b/numpy-2.3.4-cp314-cp314t-win32.whl", hash = "sha256:863e3b5f4d9915aaf1b8ec79ae560ad21f0b8d5e3adc31e73126491bb86dee1d", size = 6485782 }, - { url = "https://files.pythonhosted.org/packages/17/90/28fa6f9865181cb817c2471ee65678afa8a7e2a1fb16141473d5fa6bacc3/numpy-2.3.4-cp314-cp314t-win_amd64.whl", hash = "sha256:962064de37b9aef801d33bc579690f8bfe6c5e70e29b61783f60bcba838a14d6", size = 13113301 }, - { url = "https://files.pythonhosted.org/packages/54/23/08c002201a8e7e1f9afba93b97deceb813252d9cfd0d3351caed123dcf97/numpy-2.3.4-cp314-cp314t-win_arm64.whl", hash = "sha256:8b5a9a39c45d852b62693d9b3f3e0fe052541f804296ff401a72a1b60edafb29", size = 10547532 }, +sdist = { url = "https://files.pythonhosted.org/packages/b5/f4/098d2270d52b41f1bd7db9fc288aaa0400cb48c2a3e2af6fa365d9720947/numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a", size = 20582187, upload-time = "2025-10-15T16:18:11.77Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/7e/b72610cc91edf138bc588df5150957a4937221ca6058b825b4725c27be62/numpy-2.3.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c090d4860032b857d94144d1a9976b8e36709e40386db289aaf6672de2a81966", size = 20950335, upload-time = "2025-10-15T16:16:10.304Z" }, + { url = "https://files.pythonhosted.org/packages/3e/46/bdd3370dcea2f95ef14af79dbf81e6927102ddf1cc54adc0024d61252fd9/numpy-2.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a13fc473b6db0be619e45f11f9e81260f7302f8d180c49a22b6e6120022596b3", size = 14179878, upload-time = "2025-10-15T16:16:12.595Z" }, + { url = "https://files.pythonhosted.org/packages/ac/01/5a67cb785bda60f45415d09c2bc245433f1c68dd82eef9c9002c508b5a65/numpy-2.3.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:3634093d0b428e6c32c3a69b78e554f0cd20ee420dcad5a9f3b2a63762ce4197", size = 5108673, upload-time = "2025-10-15T16:16:14.877Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cd/8428e23a9fcebd33988f4cb61208fda832800ca03781f471f3727a820704/numpy-2.3.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:043885b4f7e6e232d7df4f51ffdef8c36320ee9d5f227b380ea636722c7ed12e", size = 6641438, upload-time = "2025-10-15T16:16:16.805Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d1/913fe563820f3c6b079f992458f7331278dcd7ba8427e8e745af37ddb44f/numpy-2.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7", size = 14281290, upload-time = "2025-10-15T16:16:18.764Z" }, + { url = "https://files.pythonhosted.org/packages/9e/7e/7d306ff7cb143e6d975cfa7eb98a93e73495c4deabb7d1b5ecf09ea0fd69/numpy-2.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc8a63918b04b8571789688b2780ab2b4a33ab44bfe8ccea36d3eba51228c953", size = 16636543, upload-time = "2025-10-15T16:16:21.072Z" }, + { url = "https://files.pythonhosted.org/packages/47/6a/8cfc486237e56ccfb0db234945552a557ca266f022d281a2f577b98e955c/numpy-2.3.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40cc556d5abbc54aabe2b1ae287042d7bdb80c08edede19f0c0afb36ae586f37", size = 16056117, upload-time = "2025-10-15T16:16:23.369Z" }, + { url = "https://files.pythonhosted.org/packages/b1/0e/42cb5e69ea901e06ce24bfcc4b5664a56f950a70efdcf221f30d9615f3f3/numpy-2.3.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ecb63014bb7f4ce653f8be7f1df8cbc6093a5a2811211770f6606cc92b5a78fd", size = 18577788, upload-time = "2025-10-15T16:16:27.496Z" }, + { url = "https://files.pythonhosted.org/packages/86/92/41c3d5157d3177559ef0a35da50f0cda7fa071f4ba2306dd36818591a5bc/numpy-2.3.4-cp313-cp313-win32.whl", hash = "sha256:e8370eb6925bb8c1c4264fec52b0384b44f675f191df91cbe0140ec9f0955646", size = 6282620, upload-time = "2025-10-15T16:16:29.811Z" }, + { url = "https://files.pythonhosted.org/packages/09/97/fd421e8bc50766665ad35536c2bb4ef916533ba1fdd053a62d96cc7c8b95/numpy-2.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:56209416e81a7893036eea03abcb91c130643eb14233b2515c90dcac963fe99d", size = 12784672, upload-time = "2025-10-15T16:16:31.589Z" }, + { url = "https://files.pythonhosted.org/packages/ad/df/5474fb2f74970ca8eb978093969b125a84cc3d30e47f82191f981f13a8a0/numpy-2.3.4-cp313-cp313-win_arm64.whl", hash = "sha256:a700a4031bc0fd6936e78a752eefb79092cecad2599ea9c8039c548bc097f9bc", size = 10196702, upload-time = "2025-10-15T16:16:33.902Z" }, + { url = "https://files.pythonhosted.org/packages/11/83/66ac031464ec1767ea3ed48ce40f615eb441072945e98693bec0bcd056cc/numpy-2.3.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:86966db35c4040fdca64f0816a1c1dd8dbd027d90fca5a57e00e1ca4cd41b879", size = 21049003, upload-time = "2025-10-15T16:16:36.101Z" }, + { url = "https://files.pythonhosted.org/packages/5f/99/5b14e0e686e61371659a1d5bebd04596b1d72227ce36eed121bb0aeab798/numpy-2.3.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:838f045478638b26c375ee96ea89464d38428c69170360b23a1a50fa4baa3562", size = 14302980, upload-time = "2025-10-15T16:16:39.124Z" }, + { url = "https://files.pythonhosted.org/packages/2c/44/e9486649cd087d9fc6920e3fc3ac2aba10838d10804b1e179fb7cbc4e634/numpy-2.3.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d7315ed1dab0286adca467377c8381cd748f3dc92235f22a7dfc42745644a96a", size = 5231472, upload-time = "2025-10-15T16:16:41.168Z" }, + { url = "https://files.pythonhosted.org/packages/3e/51/902b24fa8887e5fe2063fd61b1895a476d0bbf46811ab0c7fdf4bd127345/numpy-2.3.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:84f01a4d18b2cc4ade1814a08e5f3c907b079c847051d720fad15ce37aa930b6", size = 6739342, upload-time = "2025-10-15T16:16:43.777Z" }, + { url = "https://files.pythonhosted.org/packages/34/f1/4de9586d05b1962acdcdb1dc4af6646361a643f8c864cef7c852bf509740/numpy-2.3.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:817e719a868f0dacde4abdfc5c1910b301877970195db9ab6a5e2c4bd5b121f7", size = 14354338, upload-time = "2025-10-15T16:16:46.081Z" }, + { url = "https://files.pythonhosted.org/packages/1f/06/1c16103b425de7969d5a76bdf5ada0804b476fed05d5f9e17b777f1cbefd/numpy-2.3.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85e071da78d92a214212cacea81c6da557cab307f2c34b5f85b628e94803f9c0", size = 16702392, upload-time = "2025-10-15T16:16:48.455Z" }, + { url = "https://files.pythonhosted.org/packages/34/b2/65f4dc1b89b5322093572b6e55161bb42e3e0487067af73627f795cc9d47/numpy-2.3.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2ec646892819370cf3558f518797f16597b4e4669894a2ba712caccc9da53f1f", size = 16134998, upload-time = "2025-10-15T16:16:51.114Z" }, + { url = "https://files.pythonhosted.org/packages/d4/11/94ec578896cdb973aaf56425d6c7f2aff4186a5c00fac15ff2ec46998b46/numpy-2.3.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:035796aaaddfe2f9664b9a9372f089cfc88bd795a67bd1bfe15e6e770934cf64", size = 18651574, upload-time = "2025-10-15T16:16:53.429Z" }, + { url = "https://files.pythonhosted.org/packages/62/b7/7efa763ab33dbccf56dade36938a77345ce8e8192d6b39e470ca25ff3cd0/numpy-2.3.4-cp313-cp313t-win32.whl", hash = "sha256:fea80f4f4cf83b54c3a051f2f727870ee51e22f0248d3114b8e755d160b38cfb", size = 6413135, upload-time = "2025-10-15T16:16:55.992Z" }, + { url = "https://files.pythonhosted.org/packages/43/70/aba4c38e8400abcc2f345e13d972fb36c26409b3e644366db7649015f291/numpy-2.3.4-cp313-cp313t-win_amd64.whl", hash = "sha256:15eea9f306b98e0be91eb344a94c0e630689ef302e10c2ce5f7e11905c704f9c", size = 12928582, upload-time = "2025-10-15T16:16:57.943Z" }, + { url = "https://files.pythonhosted.org/packages/67/63/871fad5f0073fc00fbbdd7232962ea1ac40eeaae2bba66c76214f7954236/numpy-2.3.4-cp313-cp313t-win_arm64.whl", hash = "sha256:b6c231c9c2fadbae4011ca5e7e83e12dc4a5072f1a1d85a0a7b3ed754d145a40", size = 10266691, upload-time = "2025-10-15T16:17:00.048Z" }, + { url = "https://files.pythonhosted.org/packages/72/71/ae6170143c115732470ae3a2d01512870dd16e0953f8a6dc89525696069b/numpy-2.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81c3e6d8c97295a7360d367f9f8553973651b76907988bb6066376bc2252f24e", size = 20955580, upload-time = "2025-10-15T16:17:02.509Z" }, + { url = "https://files.pythonhosted.org/packages/af/39/4be9222ffd6ca8a30eda033d5f753276a9c3426c397bb137d8e19dedd200/numpy-2.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7c26b0b2bf58009ed1f38a641f3db4be8d960a417ca96d14e5b06df1506d41ff", size = 14188056, upload-time = "2025-10-15T16:17:04.873Z" }, + { url = "https://files.pythonhosted.org/packages/6c/3d/d85f6700d0a4aa4f9491030e1021c2b2b7421b2b38d01acd16734a2bfdc7/numpy-2.3.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:62b2198c438058a20b6704351b35a1d7db881812d8512d67a69c9de1f18ca05f", size = 5116555, upload-time = "2025-10-15T16:17:07.499Z" }, + { url = "https://files.pythonhosted.org/packages/bf/04/82c1467d86f47eee8a19a464c92f90a9bb68ccf14a54c5224d7031241ffb/numpy-2.3.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:9d729d60f8d53a7361707f4b68a9663c968882dd4f09e0d58c044c8bf5faee7b", size = 6643581, upload-time = "2025-10-15T16:17:09.774Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d3/c79841741b837e293f48bd7db89d0ac7a4f2503b382b78a790ef1dc778a5/numpy-2.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd0c630cf256b0a7fd9d0a11c9413b42fef5101219ce6ed5a09624f5a65392c7", size = 14299186, upload-time = "2025-10-15T16:17:11.937Z" }, + { url = "https://files.pythonhosted.org/packages/e8/7e/4a14a769741fbf237eec5a12a2cbc7a4c4e061852b6533bcb9e9a796c908/numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5e081bc082825f8b139f9e9fe42942cb4054524598aaeb177ff476cc76d09d2", size = 16638601, upload-time = "2025-10-15T16:17:14.391Z" }, + { url = "https://files.pythonhosted.org/packages/93/87/1c1de269f002ff0a41173fe01dcc925f4ecff59264cd8f96cf3b60d12c9b/numpy-2.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15fb27364ed84114438fff8aaf998c9e19adbeba08c0b75409f8c452a8692c52", size = 16074219, upload-time = "2025-10-15T16:17:17.058Z" }, + { url = "https://files.pythonhosted.org/packages/cd/28/18f72ee77408e40a76d691001ae599e712ca2a47ddd2c4f695b16c65f077/numpy-2.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:85d9fb2d8cd998c84d13a79a09cc0c1091648e848e4e6249b0ccd7f6b487fa26", size = 18576702, upload-time = "2025-10-15T16:17:19.379Z" }, + { url = "https://files.pythonhosted.org/packages/c3/76/95650169b465ececa8cf4b2e8f6df255d4bf662775e797ade2025cc51ae6/numpy-2.3.4-cp314-cp314-win32.whl", hash = "sha256:e73d63fd04e3a9d6bc187f5455d81abfad05660b212c8804bf3b407e984cd2bc", size = 6337136, upload-time = "2025-10-15T16:17:22.886Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/a231a5c43ede5d6f77ba4a91e915a87dea4aeea76560ba4d2bf185c683f0/numpy-2.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:3da3491cee49cf16157e70f607c03a217ea6647b1cea4819c4f48e53d49139b9", size = 12920542, upload-time = "2025-10-15T16:17:24.783Z" }, + { url = "https://files.pythonhosted.org/packages/0d/0c/ae9434a888f717c5ed2ff2393b3f344f0ff6f1c793519fa0c540461dc530/numpy-2.3.4-cp314-cp314-win_arm64.whl", hash = "sha256:6d9cd732068e8288dbe2717177320723ccec4fb064123f0caf9bbd90ab5be868", size = 10480213, upload-time = "2025-10-15T16:17:26.935Z" }, + { url = "https://files.pythonhosted.org/packages/83/4b/c4a5f0841f92536f6b9592694a5b5f68c9ab37b775ff342649eadf9055d3/numpy-2.3.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:22758999b256b595cf0b1d102b133bb61866ba5ceecf15f759623b64c020c9ec", size = 21052280, upload-time = "2025-10-15T16:17:29.638Z" }, + { url = "https://files.pythonhosted.org/packages/3e/80/90308845fc93b984d2cc96d83e2324ce8ad1fd6efea81b324cba4b673854/numpy-2.3.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9cb177bc55b010b19798dc5497d540dea67fd13a8d9e882b2dae71de0cf09eb3", size = 14302930, upload-time = "2025-10-15T16:17:32.384Z" }, + { url = "https://files.pythonhosted.org/packages/3d/4e/07439f22f2a3b247cec4d63a713faae55e1141a36e77fb212881f7cda3fb/numpy-2.3.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0f2bcc76f1e05e5ab58893407c63d90b2029908fa41f9f1cc51eecce936c3365", size = 5231504, upload-time = "2025-10-15T16:17:34.515Z" }, + { url = "https://files.pythonhosted.org/packages/ab/de/1e11f2547e2fe3d00482b19721855348b94ada8359aef5d40dd57bfae9df/numpy-2.3.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dc20bde86802df2ed8397a08d793da0ad7a5fd4ea3ac85d757bf5dd4ad7c252", size = 6739405, upload-time = "2025-10-15T16:17:36.128Z" }, + { url = "https://files.pythonhosted.org/packages/3b/40/8cd57393a26cebe2e923005db5134a946c62fa56a1087dc7c478f3e30837/numpy-2.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e199c087e2aa71c8f9ce1cb7a8e10677dc12457e7cc1be4798632da37c3e86e", size = 14354866, upload-time = "2025-10-15T16:17:38.884Z" }, + { url = "https://files.pythonhosted.org/packages/93/39/5b3510f023f96874ee6fea2e40dfa99313a00bf3ab779f3c92978f34aace/numpy-2.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85597b2d25ddf655495e2363fe044b0ae999b75bc4d630dc0d886484b03a5eb0", size = 16703296, upload-time = "2025-10-15T16:17:41.564Z" }, + { url = "https://files.pythonhosted.org/packages/41/0d/19bb163617c8045209c1996c4e427bccbc4bbff1e2c711f39203c8ddbb4a/numpy-2.3.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04a69abe45b49c5955923cf2c407843d1c85013b424ae8a560bba16c92fe44a0", size = 16136046, upload-time = "2025-10-15T16:17:43.901Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c1/6dba12fdf68b02a21ac411c9df19afa66bed2540f467150ca64d246b463d/numpy-2.3.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e1708fac43ef8b419c975926ce1eaf793b0c13b7356cfab6ab0dc34c0a02ac0f", size = 18652691, upload-time = "2025-10-15T16:17:46.247Z" }, + { url = "https://files.pythonhosted.org/packages/f8/73/f85056701dbbbb910c51d846c58d29fd46b30eecd2b6ba760fc8b8a1641b/numpy-2.3.4-cp314-cp314t-win32.whl", hash = "sha256:863e3b5f4d9915aaf1b8ec79ae560ad21f0b8d5e3adc31e73126491bb86dee1d", size = 6485782, upload-time = "2025-10-15T16:17:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/17/90/28fa6f9865181cb817c2471ee65678afa8a7e2a1fb16141473d5fa6bacc3/numpy-2.3.4-cp314-cp314t-win_amd64.whl", hash = "sha256:962064de37b9aef801d33bc579690f8bfe6c5e70e29b61783f60bcba838a14d6", size = 13113301, upload-time = "2025-10-15T16:17:50.938Z" }, + { url = "https://files.pythonhosted.org/packages/54/23/08c002201a8e7e1f9afba93b97deceb813252d9cfd0d3351caed123dcf97/numpy-2.3.4-cp314-cp314t-win_arm64.whl", hash = "sha256:8b5a9a39c45d852b62693d9b3f3e0fe052541f804296ff401a72a1b60edafb29", size = 10547532, upload-time = "2025-10-15T16:17:53.48Z" }, ] [[package]] @@ -1185,18 +1185,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "et-xmlfile" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464 } +sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910 }, + { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" }, ] [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] [[package]] @@ -1209,34 +1209,34 @@ dependencies = [ { name = "pytz" }, { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671 }, - { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807 }, - { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872 }, - { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371 }, - { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333 }, - { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120 }, - { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991 }, - { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227 }, - { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056 }, - { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189 }, - { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912 }, - { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160 }, - { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233 }, - { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635 }, - { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079 }, - { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049 }, - { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638 }, - { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834 }, - { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925 }, - { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071 }, - { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504 }, - { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702 }, - { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535 }, - { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582 }, - { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963 }, - { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175 }, +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, ] [[package]] @@ -1248,76 +1248,76 @@ dependencies = [ { name = "sqlalchemy" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/fe/84f4d06ac3e6038384847dc1d5c8b956f61b780f69509d177107b550c7b9/paracelsus-0.12.0.tar.gz", hash = "sha256:f1d8f584ebc445db99a2906f97ff55f36ae663c104320dd4a6b5b78b4fa24dce", size = 83664 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fe/84f4d06ac3e6038384847dc1d5c8b956f61b780f69509d177107b550c7b9/paracelsus-0.12.0.tar.gz", hash = "sha256:f1d8f584ebc445db99a2906f97ff55f36ae663c104320dd4a6b5b78b4fa24dce", size = 83664, upload-time = "2025-10-07T12:45:41.112Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/60/9062e4072c16750b6b01bac9c55b329b249ee7c970d61c128049be197d7a/paracelsus-0.12.0-py3-none-any.whl", hash = "sha256:01f5a508174d06a86d53374215a0c85962498361ac3f0bd3450023760d3b3836", size = 81236 }, + { url = "https://files.pythonhosted.org/packages/b3/60/9062e4072c16750b6b01bac9c55b329b249ee7c970d61c128049be197d7a/paracelsus-0.12.0-py3-none-any.whl", hash = "sha256:01f5a508174d06a86d53374215a0c85962498361ac3f0bd3450023760d3b3836", size = 81236, upload-time = "2025-10-07T12:45:39.929Z" }, ] [[package]] name = "pillow" version = "12.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/f2/de993bb2d21b33a98d031ecf6a978e4b61da207bef02f7b43093774c480d/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", size = 4045493 }, - { url = "https://files.pythonhosted.org/packages/0e/b6/bc8d0c4c9f6f111a783d045310945deb769b806d7574764234ffd50bc5ea/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", size = 4120461 }, - { url = "https://files.pythonhosted.org/packages/5d/57/d60d343709366a353dc56adb4ee1e7d8a2cc34e3fbc22905f4167cfec119/pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", size = 3576912 }, - { url = "https://files.pythonhosted.org/packages/a4/a4/a0a31467e3f83b94d37568294b01d22b43ae3c5d85f2811769b9c66389dd/pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", size = 5249132 }, - { url = "https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", size = 4650099 }, - { url = "https://files.pythonhosted.org/packages/fc/bd/69ed99fd46a8dba7c1887156d3572fe4484e3f031405fcc5a92e31c04035/pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", size = 6230808 }, - { url = "https://files.pythonhosted.org/packages/ea/94/8fad659bcdbf86ed70099cb60ae40be6acca434bbc8c4c0d4ef356d7e0de/pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", size = 8037804 }, - { url = "https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", size = 6345553 }, - { url = "https://files.pythonhosted.org/packages/38/57/755dbd06530a27a5ed74f8cb0a7a44a21722ebf318edbe67ddbd7fb28f88/pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", size = 7037729 }, - { url = "https://files.pythonhosted.org/packages/ca/b6/7e94f4c41d238615674d06ed677c14883103dce1c52e4af16f000338cfd7/pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", size = 6459789 }, - { url = "https://files.pythonhosted.org/packages/9c/14/4448bb0b5e0f22dd865290536d20ec8a23b64e2d04280b89139f09a36bb6/pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", size = 7130917 }, - { url = "https://files.pythonhosted.org/packages/dd/ca/16c6926cc1c015845745d5c16c9358e24282f1e588237a4c36d2b30f182f/pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", size = 6302391 }, - { url = "https://files.pythonhosted.org/packages/6d/2a/dd43dcfd6dae9b6a49ee28a8eedb98c7d5ff2de94a5d834565164667b97b/pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", size = 7007477 }, - { url = "https://files.pythonhosted.org/packages/77/f0/72ea067f4b5ae5ead653053212af05ce3705807906ba3f3e8f58ddf617e6/pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", size = 2435918 }, - { url = "https://files.pythonhosted.org/packages/f5/5e/9046b423735c21f0487ea6cb5b10f89ea8f8dfbe32576fe052b5ba9d4e5b/pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", size = 5251406 }, - { url = "https://files.pythonhosted.org/packages/12/66/982ceebcdb13c97270ef7a56c3969635b4ee7cd45227fa707c94719229c5/pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", size = 4653218 }, - { url = "https://files.pythonhosted.org/packages/16/b3/81e625524688c31859450119bf12674619429cab3119eec0e30a7a1029cb/pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", size = 6266564 }, - { url = "https://files.pythonhosted.org/packages/98/59/dfb38f2a41240d2408096e1a76c671d0a105a4a8471b1871c6902719450c/pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", size = 8069260 }, - { url = "https://files.pythonhosted.org/packages/dc/3d/378dbea5cd1874b94c312425ca77b0f47776c78e0df2df751b820c8c1d6c/pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", size = 6379248 }, - { url = "https://files.pythonhosted.org/packages/84/b0/d525ef47d71590f1621510327acec75ae58c721dc071b17d8d652ca494d8/pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", size = 7066043 }, - { url = "https://files.pythonhosted.org/packages/61/2c/aced60e9cf9d0cde341d54bf7932c9ffc33ddb4a1595798b3a5150c7ec4e/pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", size = 6490915 }, - { url = "https://files.pythonhosted.org/packages/ef/26/69dcb9b91f4e59f8f34b2332a4a0a951b44f547c4ed39d3e4dcfcff48f89/pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", size = 7157998 }, - { url = "https://files.pythonhosted.org/packages/61/2b/726235842220ca95fa441ddf55dd2382b52ab5b8d9c0596fe6b3f23dafe8/pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", size = 6306201 }, - { url = "https://files.pythonhosted.org/packages/c0/3d/2afaf4e840b2df71344ababf2f8edd75a705ce500e5dc1e7227808312ae1/pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", size = 7013165 }, - { url = "https://files.pythonhosted.org/packages/6f/75/3fa09aa5cf6ed04bee3fa575798ddf1ce0bace8edb47249c798077a81f7f/pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", size = 2437834 }, - { url = "https://files.pythonhosted.org/packages/54/2a/9a8c6ba2c2c07b71bec92cf63e03370ca5e5f5c5b119b742bcc0cde3f9c5/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", size = 4045531 }, - { url = "https://files.pythonhosted.org/packages/84/54/836fdbf1bfb3d66a59f0189ff0b9f5f666cee09c6188309300df04ad71fa/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", size = 4120554 }, - { url = "https://files.pythonhosted.org/packages/0d/cd/16aec9f0da4793e98e6b54778a5fbce4f375c6646fe662e80600b8797379/pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", size = 3576812 }, - { url = "https://files.pythonhosted.org/packages/f6/b7/13957fda356dc46339298b351cae0d327704986337c3c69bb54628c88155/pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", size = 5252689 }, - { url = "https://files.pythonhosted.org/packages/fc/f5/eae31a306341d8f331f43edb2e9122c7661b975433de5e447939ae61c5da/pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", size = 4650186 }, - { url = "https://files.pythonhosted.org/packages/86/62/2a88339aa40c4c77e79108facbd307d6091e2c0eb5b8d3cf4977cfca2fe6/pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", size = 6230308 }, - { url = "https://files.pythonhosted.org/packages/c7/33/5425a8992bcb32d1cb9fa3dd39a89e613d09a22f2c8083b7bf43c455f760/pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", size = 8039222 }, - { url = "https://files.pythonhosted.org/packages/d8/61/3f5d3b35c5728f37953d3eec5b5f3e77111949523bd2dd7f31a851e50690/pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", size = 6346657 }, - { url = "https://files.pythonhosted.org/packages/3a/be/ee90a3d79271227e0f0a33c453531efd6ed14b2e708596ba5dd9be948da3/pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", size = 7038482 }, - { url = "https://files.pythonhosted.org/packages/44/34/a16b6a4d1ad727de390e9bd9f19f5f669e079e5826ec0f329010ddea492f/pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", size = 6461416 }, - { url = "https://files.pythonhosted.org/packages/b6/39/1aa5850d2ade7d7ba9f54e4e4c17077244ff7a2d9e25998c38a29749eb3f/pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", size = 7131584 }, - { url = "https://files.pythonhosted.org/packages/bf/db/4fae862f8fad0167073a7733973bfa955f47e2cac3dc3e3e6257d10fab4a/pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", size = 6400621 }, - { url = "https://files.pythonhosted.org/packages/2b/24/b350c31543fb0107ab2599464d7e28e6f856027aadda995022e695313d94/pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", size = 7142916 }, - { url = "https://files.pythonhosted.org/packages/0f/9b/0ba5a6fd9351793996ef7487c4fdbde8d3f5f75dbedc093bb598648fddf0/pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", size = 2523836 }, - { url = "https://files.pythonhosted.org/packages/f5/7a/ceee0840aebc579af529b523d530840338ecf63992395842e54edc805987/pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", size = 5255092 }, - { url = "https://files.pythonhosted.org/packages/44/76/20776057b4bfd1aef4eeca992ebde0f53a4dce874f3ae693d0ec90a4f79b/pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", size = 4653158 }, - { url = "https://files.pythonhosted.org/packages/82/3f/d9ff92ace07be8836b4e7e87e6a4c7a8318d47c2f1463ffcf121fc57d9cb/pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1", size = 6267882 }, - { url = "https://files.pythonhosted.org/packages/9f/7a/4f7ff87f00d3ad33ba21af78bfcd2f032107710baf8280e3722ceec28cda/pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", size = 8071001 }, - { url = "https://files.pythonhosted.org/packages/75/87/fcea108944a52dad8cca0715ae6247e271eb80459364a98518f1e4f480c1/pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", size = 6380146 }, - { url = "https://files.pythonhosted.org/packages/91/52/0d31b5e571ef5fd111d2978b84603fce26aba1b6092f28e941cb46570745/pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", size = 7067344 }, - { url = "https://files.pythonhosted.org/packages/7b/f4/2dd3d721f875f928d48e83bb30a434dee75a2531bca839bb996bb0aa5a91/pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", size = 6491864 }, - { url = "https://files.pythonhosted.org/packages/30/4b/667dfcf3d61fc309ba5a15b141845cece5915e39b99c1ceab0f34bf1d124/pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", size = 7158911 }, - { url = "https://files.pythonhosted.org/packages/a2/2f/16cabcc6426c32218ace36bf0d55955e813f2958afddbf1d391849fee9d1/pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", size = 6408045 }, - { url = "https://files.pythonhosted.org/packages/35/73/e29aa0c9c666cf787628d3f0dcf379f4791fba79f4936d02f8b37165bdf8/pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", size = 7148282 }, - { url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630 }, +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828, upload-time = "2025-10-15T18:24:14.008Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/f2/de993bb2d21b33a98d031ecf6a978e4b61da207bef02f7b43093774c480d/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", size = 4045493, upload-time = "2025-10-15T18:22:25.758Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b6/bc8d0c4c9f6f111a783d045310945deb769b806d7574764234ffd50bc5ea/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", size = 4120461, upload-time = "2025-10-15T18:22:27.286Z" }, + { url = "https://files.pythonhosted.org/packages/5d/57/d60d343709366a353dc56adb4ee1e7d8a2cc34e3fbc22905f4167cfec119/pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", size = 3576912, upload-time = "2025-10-15T18:22:28.751Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a4/a0a31467e3f83b94d37568294b01d22b43ae3c5d85f2811769b9c66389dd/pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", size = 5249132, upload-time = "2025-10-15T18:22:30.641Z" }, + { url = "https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", size = 4650099, upload-time = "2025-10-15T18:22:32.73Z" }, + { url = "https://files.pythonhosted.org/packages/fc/bd/69ed99fd46a8dba7c1887156d3572fe4484e3f031405fcc5a92e31c04035/pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", size = 6230808, upload-time = "2025-10-15T18:22:34.337Z" }, + { url = "https://files.pythonhosted.org/packages/ea/94/8fad659bcdbf86ed70099cb60ae40be6acca434bbc8c4c0d4ef356d7e0de/pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", size = 8037804, upload-time = "2025-10-15T18:22:36.402Z" }, + { url = "https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", size = 6345553, upload-time = "2025-10-15T18:22:38.066Z" }, + { url = "https://files.pythonhosted.org/packages/38/57/755dbd06530a27a5ed74f8cb0a7a44a21722ebf318edbe67ddbd7fb28f88/pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", size = 7037729, upload-time = "2025-10-15T18:22:39.769Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/7e94f4c41d238615674d06ed677c14883103dce1c52e4af16f000338cfd7/pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", size = 6459789, upload-time = "2025-10-15T18:22:41.437Z" }, + { url = "https://files.pythonhosted.org/packages/9c/14/4448bb0b5e0f22dd865290536d20ec8a23b64e2d04280b89139f09a36bb6/pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", size = 7130917, upload-time = "2025-10-15T18:22:43.152Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ca/16c6926cc1c015845745d5c16c9358e24282f1e588237a4c36d2b30f182f/pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", size = 6302391, upload-time = "2025-10-15T18:22:44.753Z" }, + { url = "https://files.pythonhosted.org/packages/6d/2a/dd43dcfd6dae9b6a49ee28a8eedb98c7d5ff2de94a5d834565164667b97b/pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", size = 7007477, upload-time = "2025-10-15T18:22:46.838Z" }, + { url = "https://files.pythonhosted.org/packages/77/f0/72ea067f4b5ae5ead653053212af05ce3705807906ba3f3e8f58ddf617e6/pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", size = 2435918, upload-time = "2025-10-15T18:22:48.399Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5e/9046b423735c21f0487ea6cb5b10f89ea8f8dfbe32576fe052b5ba9d4e5b/pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", size = 5251406, upload-time = "2025-10-15T18:22:49.905Z" }, + { url = "https://files.pythonhosted.org/packages/12/66/982ceebcdb13c97270ef7a56c3969635b4ee7cd45227fa707c94719229c5/pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", size = 4653218, upload-time = "2025-10-15T18:22:51.587Z" }, + { url = "https://files.pythonhosted.org/packages/16/b3/81e625524688c31859450119bf12674619429cab3119eec0e30a7a1029cb/pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", size = 6266564, upload-time = "2025-10-15T18:22:53.215Z" }, + { url = "https://files.pythonhosted.org/packages/98/59/dfb38f2a41240d2408096e1a76c671d0a105a4a8471b1871c6902719450c/pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", size = 8069260, upload-time = "2025-10-15T18:22:54.933Z" }, + { url = "https://files.pythonhosted.org/packages/dc/3d/378dbea5cd1874b94c312425ca77b0f47776c78e0df2df751b820c8c1d6c/pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", size = 6379248, upload-time = "2025-10-15T18:22:56.605Z" }, + { url = "https://files.pythonhosted.org/packages/84/b0/d525ef47d71590f1621510327acec75ae58c721dc071b17d8d652ca494d8/pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", size = 7066043, upload-time = "2025-10-15T18:22:58.53Z" }, + { url = "https://files.pythonhosted.org/packages/61/2c/aced60e9cf9d0cde341d54bf7932c9ffc33ddb4a1595798b3a5150c7ec4e/pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", size = 6490915, upload-time = "2025-10-15T18:23:00.582Z" }, + { url = "https://files.pythonhosted.org/packages/ef/26/69dcb9b91f4e59f8f34b2332a4a0a951b44f547c4ed39d3e4dcfcff48f89/pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", size = 7157998, upload-time = "2025-10-15T18:23:02.627Z" }, + { url = "https://files.pythonhosted.org/packages/61/2b/726235842220ca95fa441ddf55dd2382b52ab5b8d9c0596fe6b3f23dafe8/pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", size = 6306201, upload-time = "2025-10-15T18:23:04.709Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3d/2afaf4e840b2df71344ababf2f8edd75a705ce500e5dc1e7227808312ae1/pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", size = 7013165, upload-time = "2025-10-15T18:23:06.46Z" }, + { url = "https://files.pythonhosted.org/packages/6f/75/3fa09aa5cf6ed04bee3fa575798ddf1ce0bace8edb47249c798077a81f7f/pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", size = 2437834, upload-time = "2025-10-15T18:23:08.194Z" }, + { url = "https://files.pythonhosted.org/packages/54/2a/9a8c6ba2c2c07b71bec92cf63e03370ca5e5f5c5b119b742bcc0cde3f9c5/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", size = 4045531, upload-time = "2025-10-15T18:23:10.121Z" }, + { url = "https://files.pythonhosted.org/packages/84/54/836fdbf1bfb3d66a59f0189ff0b9f5f666cee09c6188309300df04ad71fa/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", size = 4120554, upload-time = "2025-10-15T18:23:12.14Z" }, + { url = "https://files.pythonhosted.org/packages/0d/cd/16aec9f0da4793e98e6b54778a5fbce4f375c6646fe662e80600b8797379/pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", size = 3576812, upload-time = "2025-10-15T18:23:13.962Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b7/13957fda356dc46339298b351cae0d327704986337c3c69bb54628c88155/pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", size = 5252689, upload-time = "2025-10-15T18:23:15.562Z" }, + { url = "https://files.pythonhosted.org/packages/fc/f5/eae31a306341d8f331f43edb2e9122c7661b975433de5e447939ae61c5da/pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", size = 4650186, upload-time = "2025-10-15T18:23:17.379Z" }, + { url = "https://files.pythonhosted.org/packages/86/62/2a88339aa40c4c77e79108facbd307d6091e2c0eb5b8d3cf4977cfca2fe6/pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", size = 6230308, upload-time = "2025-10-15T18:23:18.971Z" }, + { url = "https://files.pythonhosted.org/packages/c7/33/5425a8992bcb32d1cb9fa3dd39a89e613d09a22f2c8083b7bf43c455f760/pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", size = 8039222, upload-time = "2025-10-15T18:23:20.909Z" }, + { url = "https://files.pythonhosted.org/packages/d8/61/3f5d3b35c5728f37953d3eec5b5f3e77111949523bd2dd7f31a851e50690/pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", size = 6346657, upload-time = "2025-10-15T18:23:23.077Z" }, + { url = "https://files.pythonhosted.org/packages/3a/be/ee90a3d79271227e0f0a33c453531efd6ed14b2e708596ba5dd9be948da3/pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", size = 7038482, upload-time = "2025-10-15T18:23:25.005Z" }, + { url = "https://files.pythonhosted.org/packages/44/34/a16b6a4d1ad727de390e9bd9f19f5f669e079e5826ec0f329010ddea492f/pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", size = 6461416, upload-time = "2025-10-15T18:23:27.009Z" }, + { url = "https://files.pythonhosted.org/packages/b6/39/1aa5850d2ade7d7ba9f54e4e4c17077244ff7a2d9e25998c38a29749eb3f/pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", size = 7131584, upload-time = "2025-10-15T18:23:29.752Z" }, + { url = "https://files.pythonhosted.org/packages/bf/db/4fae862f8fad0167073a7733973bfa955f47e2cac3dc3e3e6257d10fab4a/pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", size = 6400621, upload-time = "2025-10-15T18:23:32.06Z" }, + { url = "https://files.pythonhosted.org/packages/2b/24/b350c31543fb0107ab2599464d7e28e6f856027aadda995022e695313d94/pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", size = 7142916, upload-time = "2025-10-15T18:23:34.71Z" }, + { url = "https://files.pythonhosted.org/packages/0f/9b/0ba5a6fd9351793996ef7487c4fdbde8d3f5f75dbedc093bb598648fddf0/pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", size = 2523836, upload-time = "2025-10-15T18:23:36.967Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7a/ceee0840aebc579af529b523d530840338ecf63992395842e54edc805987/pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", size = 5255092, upload-time = "2025-10-15T18:23:38.573Z" }, + { url = "https://files.pythonhosted.org/packages/44/76/20776057b4bfd1aef4eeca992ebde0f53a4dce874f3ae693d0ec90a4f79b/pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", size = 4653158, upload-time = "2025-10-15T18:23:40.238Z" }, + { url = "https://files.pythonhosted.org/packages/82/3f/d9ff92ace07be8836b4e7e87e6a4c7a8318d47c2f1463ffcf121fc57d9cb/pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1", size = 6267882, upload-time = "2025-10-15T18:23:42.434Z" }, + { url = "https://files.pythonhosted.org/packages/9f/7a/4f7ff87f00d3ad33ba21af78bfcd2f032107710baf8280e3722ceec28cda/pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", size = 8071001, upload-time = "2025-10-15T18:23:44.29Z" }, + { url = "https://files.pythonhosted.org/packages/75/87/fcea108944a52dad8cca0715ae6247e271eb80459364a98518f1e4f480c1/pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", size = 6380146, upload-time = "2025-10-15T18:23:46.065Z" }, + { url = "https://files.pythonhosted.org/packages/91/52/0d31b5e571ef5fd111d2978b84603fce26aba1b6092f28e941cb46570745/pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", size = 7067344, upload-time = "2025-10-15T18:23:47.898Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f4/2dd3d721f875f928d48e83bb30a434dee75a2531bca839bb996bb0aa5a91/pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", size = 6491864, upload-time = "2025-10-15T18:23:49.607Z" }, + { url = "https://files.pythonhosted.org/packages/30/4b/667dfcf3d61fc309ba5a15b141845cece5915e39b99c1ceab0f34bf1d124/pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", size = 7158911, upload-time = "2025-10-15T18:23:51.351Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2f/16cabcc6426c32218ace36bf0d55955e813f2958afddbf1d391849fee9d1/pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", size = 6408045, upload-time = "2025-10-15T18:23:53.177Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/e29aa0c9c666cf787628d3f0dcf379f4791fba79f4936d02f8b37165bdf8/pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", size = 7148282, upload-time = "2025-10-15T18:23:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630, upload-time = "2025-10-15T18:23:57.149Z" }, ] [[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 } +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 }, + { 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]] @@ -1327,24 +1327,24 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142 } +sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142, upload-time = "2025-03-10T15:54:38.843Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163 }, + { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163, upload-time = "2025-03-10T15:54:37.335Z" }, ] [[package]] name = "protobuf" version = "6.33.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/ff/64a6c8f420818bb873713988ca5492cba3a7946be57e027ac63495157d97/protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954", size = 443463 } +sdist = { url = "https://files.pythonhosted.org/packages/19/ff/64a6c8f420818bb873713988ca5492cba3a7946be57e027ac63495157d97/protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954", size = 443463, upload-time = "2025-10-15T20:39:52.159Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/ee/52b3fa8feb6db4a833dfea4943e175ce645144532e8a90f72571ad85df4e/protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035", size = 425593 }, - { url = "https://files.pythonhosted.org/packages/7b/c6/7a465f1825872c55e0341ff4a80198743f73b69ce5d43ab18043699d1d81/protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee", size = 436882 }, - { url = "https://files.pythonhosted.org/packages/e1/a9/b6eee662a6951b9c3640e8e452ab3e09f117d99fc10baa32d1581a0d4099/protobuf-6.33.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455", size = 427521 }, - { url = "https://files.pythonhosted.org/packages/10/35/16d31e0f92c6d2f0e77c2a3ba93185130ea13053dd16200a57434c882f2b/protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90", size = 324445 }, - { url = "https://files.pythonhosted.org/packages/e6/eb/2a981a13e35cda8b75b5585aaffae2eb904f8f351bdd3870769692acbd8a/protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298", size = 339159 }, - { url = "https://files.pythonhosted.org/packages/21/51/0b1cbad62074439b867b4e04cc09b93f6699d78fd191bed2bbb44562e077/protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef", size = 323172 }, - { url = "https://files.pythonhosted.org/packages/07/d1/0a28c21707807c6aacd5dc9c3704b2aa1effbf37adebd8caeaf68b17a636/protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995", size = 170477 }, + { url = "https://files.pythonhosted.org/packages/7e/ee/52b3fa8feb6db4a833dfea4943e175ce645144532e8a90f72571ad85df4e/protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035", size = 425593, upload-time = "2025-10-15T20:39:40.29Z" }, + { url = "https://files.pythonhosted.org/packages/7b/c6/7a465f1825872c55e0341ff4a80198743f73b69ce5d43ab18043699d1d81/protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee", size = 436882, upload-time = "2025-10-15T20:39:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/e1/a9/b6eee662a6951b9c3640e8e452ab3e09f117d99fc10baa32d1581a0d4099/protobuf-6.33.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455", size = 427521, upload-time = "2025-10-15T20:39:43.803Z" }, + { url = "https://files.pythonhosted.org/packages/10/35/16d31e0f92c6d2f0e77c2a3ba93185130ea13053dd16200a57434c882f2b/protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90", size = 324445, upload-time = "2025-10-15T20:39:44.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/eb/2a981a13e35cda8b75b5585aaffae2eb904f8f351bdd3870769692acbd8a/protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298", size = 339159, upload-time = "2025-10-15T20:39:46.186Z" }, + { url = "https://files.pythonhosted.org/packages/21/51/0b1cbad62074439b867b4e04cc09b93f6699d78fd191bed2bbb44562e077/protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef", size = 323172, upload-time = "2025-10-15T20:39:47.465Z" }, + { url = "https://files.pythonhosted.org/packages/07/d1/0a28c21707807c6aacd5dc9c3704b2aa1effbf37adebd8caeaf68b17a636/protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995", size = 170477, upload-time = "2025-10-15T20:39:51.311Z" }, ] [[package]] @@ -1354,9 +1354,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/77/c72d10262b872617e509a0c60445afcc4ce2cd5cd6bc1c97700246d69c85/psycopg-3.2.12.tar.gz", hash = "sha256:85c08d6f6e2a897b16280e0ff6406bef29b1327c045db06d21f364d7cd5da90b", size = 160642 } +sdist = { url = "https://files.pythonhosted.org/packages/a8/77/c72d10262b872617e509a0c60445afcc4ce2cd5cd6bc1c97700246d69c85/psycopg-3.2.12.tar.gz", hash = "sha256:85c08d6f6e2a897b16280e0ff6406bef29b1327c045db06d21f364d7cd5da90b", size = 160642, upload-time = "2025-10-26T00:46:03.045Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/28/8c4f90e415411dc9c78d6ba10b549baa324659907c13f64bfe3779d4066c/psycopg-3.2.12-py3-none-any.whl", hash = "sha256:8a1611a2d4c16ae37eada46438be9029a35bb959bb50b3d0e1e93c0f3d54c9ee", size = 206765 }, + { url = "https://files.pythonhosted.org/packages/c8/28/8c4f90e415411dc9c78d6ba10b549baa324659907c13f64bfe3779d4066c/psycopg-3.2.12-py3-none-any.whl", hash = "sha256:8a1611a2d4c16ae37eada46438be9029a35bb959bb50b3d0e1e93c0f3d54c9ee", size = 206765, upload-time = "2025-10-26T00:10:42.173Z" }, ] [package.optional-dependencies] @@ -1369,33 +1369,33 @@ name = "psycopg-binary" version = "3.2.12" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/0b/9d480aba4a4864832c29e6fc94ddd34d9927c276448eb3b56ffe24ed064c/psycopg_binary-3.2.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:442f20153415f374ae5753ca618637611a41a3c58c56d16ce55f845d76a3cf7b", size = 4017829 }, - { url = "https://files.pythonhosted.org/packages/a4/f3/0d294b30349bde24a46741a1f27a10e8ab81e9f4118d27c2fe592acfb42a/psycopg_binary-3.2.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:79de3cc5adbf51677009a8fda35ac9e9e3686d5595ab4b0c43ec7099ece6aeb5", size = 4089835 }, - { url = "https://files.pythonhosted.org/packages/82/d4/ff82e318e5a55d6951b278d3af7b4c7c1b19344e3a3722b6613f156a38ea/psycopg_binary-3.2.12-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:095ccda59042a1239ac2fefe693a336cb5cecf8944a8d9e98b07f07e94e2b78d", size = 4625474 }, - { url = "https://files.pythonhosted.org/packages/b1/e8/2c9df6475a5ab6d614d516f4497c568d84f7d6c21d0e11444468c9786c9f/psycopg_binary-3.2.12-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:efab679a2c7d1bf7d0ec0e1ecb47fe764945eff75bb4321f2e699b30a12db9b3", size = 4720350 }, - { url = "https://files.pythonhosted.org/packages/74/f5/7aec81b0c41985dc006e2d5822486ad4b7c2a1a97a5a05e37dc2adaf1512/psycopg_binary-3.2.12-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d369e79ad9647fc8217cbb51bbbf11f9a1ffca450be31d005340157ffe8e91b3", size = 4411621 }, - { url = "https://files.pythonhosted.org/packages/fc/15/d3cb41b8fa9d5f14320ab250545fbb66f9ddb481e448e618902672a806c0/psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eedc410f82007038030650aa58f620f9fe0009b9d6b04c3dc71cbd3bae5b2675", size = 3863081 }, - { url = "https://files.pythonhosted.org/packages/69/8a/72837664e63e3cd3aa145cedcf29e5c21257579739aba78ab7eb668f7d9c/psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f3bae4be7f6781bf6c9576eedcd5e1bb74468126fa6de991e47cdb1a8ea3a42a", size = 3537428 }, - { url = "https://files.pythonhosted.org/packages/cc/7e/1b78ae38e7d69e6d7fb1e2dcce101493f5fa429480bac3a68b876c9b1635/psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8ffe75fe6be902dadd439adf4228c98138a992088e073ede6dd34e7235f4e03e", size = 3585981 }, - { url = "https://files.pythonhosted.org/packages/a3/f8/245b4868b2dac46c3fb6383b425754ae55df1910c826d305ed414da03777/psycopg_binary-3.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:2598d0e4f2f258da13df0560187b3f1dfc9b8688c46b9d90176360ae5212c3fc", size = 2912929 }, - { url = "https://files.pythonhosted.org/packages/5c/5b/76fbb40b981b73b285a00dccafc38cf67b7a9b3f7d4f2025dda7b896e7ef/psycopg_binary-3.2.12-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dc68094e00a5a7e8c20de1d3a0d5e404a27f522e18f8eb62bbbc9f865c3c81ef", size = 4016868 }, - { url = "https://files.pythonhosted.org/packages/0e/08/8841ae3e2d1a3228e79eaaf5b7f991d15f0a231bb5031a114305b19724b1/psycopg_binary-3.2.12-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2d55009eeddbef54c711093c986daaf361d2c4210aaa1ee905075a3b97a62441", size = 4090508 }, - { url = "https://files.pythonhosted.org/packages/05/de/a41f62230cf4095ae4547eceada218cf28c17e7f94376913c1c8dde9546f/psycopg_binary-3.2.12-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:66a031f22e4418016990446d3e38143826f03ad811b9f78f58e2afbc1d343f7a", size = 4629788 }, - { url = "https://files.pythonhosted.org/packages/45/19/529d92134eae44475f781a86d58cdf3edd0953e17c69762abf387a9f2636/psycopg_binary-3.2.12-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:58ed30d33c25d7dc8d2f06285e88493147c2a660cc94713e4b563a99efb80a1f", size = 4724124 }, - { url = "https://files.pythonhosted.org/packages/5c/f5/97344e87065f7c9713ce213a2cff7732936ec3af6622e4b2a88715a953f2/psycopg_binary-3.2.12-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e0b5ccd03ca4749b8f66f38608ccbcb415cbd130d02de5eda80d042b83bee90e", size = 4411340 }, - { url = "https://files.pythonhosted.org/packages/b1/c2/34bce068f6bfb4c2e7bb1187bb64a3f3be254702b158c4ad05eacc0055cf/psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:909de94de7dd4d6086098a5755562207114c9638ec42c52d84c8a440c45fe084", size = 3867815 }, - { url = "https://files.pythonhosted.org/packages/d1/a1/c647e01ab162e6bfa52380e23e486215e9d28ffd31e9cf3cb1e9ca59008b/psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:7130effd0517881f3a852eff98729d51034128f0737f64f0d1c7ea8343d77bd7", size = 3541756 }, - { url = "https://files.pythonhosted.org/packages/6b/d0/795bdaa8c946a7b7126bf7ca8d4371eaaa613093e3ec341a0e50f52cbee2/psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:89b3c5201ca616d69ca0c3c0003ca18f7170a679c445c7e386ebfb4f29aa738e", size = 3587950 }, - { url = "https://files.pythonhosted.org/packages/53/cf/10c3e95827a3ca8af332dfc471befec86e15a14dc83cee893c49a4910dad/psycopg_binary-3.2.12-cp314-cp314-win_amd64.whl", hash = "sha256:48a8e29f3e38fcf8d393b8fe460d83e39c107ad7e5e61cd3858a7569e0554a39", size = 3005787 }, + { url = "https://files.pythonhosted.org/packages/b2/0b/9d480aba4a4864832c29e6fc94ddd34d9927c276448eb3b56ffe24ed064c/psycopg_binary-3.2.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:442f20153415f374ae5753ca618637611a41a3c58c56d16ce55f845d76a3cf7b", size = 4017829, upload-time = "2025-10-26T00:26:27.031Z" }, + { url = "https://files.pythonhosted.org/packages/a4/f3/0d294b30349bde24a46741a1f27a10e8ab81e9f4118d27c2fe592acfb42a/psycopg_binary-3.2.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:79de3cc5adbf51677009a8fda35ac9e9e3686d5595ab4b0c43ec7099ece6aeb5", size = 4089835, upload-time = "2025-10-26T00:27:01.392Z" }, + { url = "https://files.pythonhosted.org/packages/82/d4/ff82e318e5a55d6951b278d3af7b4c7c1b19344e3a3722b6613f156a38ea/psycopg_binary-3.2.12-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:095ccda59042a1239ac2fefe693a336cb5cecf8944a8d9e98b07f07e94e2b78d", size = 4625474, upload-time = "2025-10-26T00:27:40.34Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e8/2c9df6475a5ab6d614d516f4497c568d84f7d6c21d0e11444468c9786c9f/psycopg_binary-3.2.12-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:efab679a2c7d1bf7d0ec0e1ecb47fe764945eff75bb4321f2e699b30a12db9b3", size = 4720350, upload-time = "2025-10-26T00:28:20.104Z" }, + { url = "https://files.pythonhosted.org/packages/74/f5/7aec81b0c41985dc006e2d5822486ad4b7c2a1a97a5a05e37dc2adaf1512/psycopg_binary-3.2.12-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d369e79ad9647fc8217cbb51bbbf11f9a1ffca450be31d005340157ffe8e91b3", size = 4411621, upload-time = "2025-10-26T00:28:59.104Z" }, + { url = "https://files.pythonhosted.org/packages/fc/15/d3cb41b8fa9d5f14320ab250545fbb66f9ddb481e448e618902672a806c0/psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eedc410f82007038030650aa58f620f9fe0009b9d6b04c3dc71cbd3bae5b2675", size = 3863081, upload-time = "2025-10-26T00:29:31.235Z" }, + { url = "https://files.pythonhosted.org/packages/69/8a/72837664e63e3cd3aa145cedcf29e5c21257579739aba78ab7eb668f7d9c/psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f3bae4be7f6781bf6c9576eedcd5e1bb74468126fa6de991e47cdb1a8ea3a42a", size = 3537428, upload-time = "2025-10-26T00:30:01.465Z" }, + { url = "https://files.pythonhosted.org/packages/cc/7e/1b78ae38e7d69e6d7fb1e2dcce101493f5fa429480bac3a68b876c9b1635/psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8ffe75fe6be902dadd439adf4228c98138a992088e073ede6dd34e7235f4e03e", size = 3585981, upload-time = "2025-10-26T00:30:31.635Z" }, + { url = "https://files.pythonhosted.org/packages/a3/f8/245b4868b2dac46c3fb6383b425754ae55df1910c826d305ed414da03777/psycopg_binary-3.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:2598d0e4f2f258da13df0560187b3f1dfc9b8688c46b9d90176360ae5212c3fc", size = 2912929, upload-time = "2025-10-26T00:30:56.413Z" }, + { url = "https://files.pythonhosted.org/packages/5c/5b/76fbb40b981b73b285a00dccafc38cf67b7a9b3f7d4f2025dda7b896e7ef/psycopg_binary-3.2.12-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dc68094e00a5a7e8c20de1d3a0d5e404a27f522e18f8eb62bbbc9f865c3c81ef", size = 4016868, upload-time = "2025-10-26T00:31:29.974Z" }, + { url = "https://files.pythonhosted.org/packages/0e/08/8841ae3e2d1a3228e79eaaf5b7f991d15f0a231bb5031a114305b19724b1/psycopg_binary-3.2.12-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2d55009eeddbef54c711093c986daaf361d2c4210aaa1ee905075a3b97a62441", size = 4090508, upload-time = "2025-10-26T00:32:04.192Z" }, + { url = "https://files.pythonhosted.org/packages/05/de/a41f62230cf4095ae4547eceada218cf28c17e7f94376913c1c8dde9546f/psycopg_binary-3.2.12-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:66a031f22e4418016990446d3e38143826f03ad811b9f78f58e2afbc1d343f7a", size = 4629788, upload-time = "2025-10-26T00:32:43.28Z" }, + { url = "https://files.pythonhosted.org/packages/45/19/529d92134eae44475f781a86d58cdf3edd0953e17c69762abf387a9f2636/psycopg_binary-3.2.12-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:58ed30d33c25d7dc8d2f06285e88493147c2a660cc94713e4b563a99efb80a1f", size = 4724124, upload-time = "2025-10-26T00:33:22.594Z" }, + { url = "https://files.pythonhosted.org/packages/5c/f5/97344e87065f7c9713ce213a2cff7732936ec3af6622e4b2a88715a953f2/psycopg_binary-3.2.12-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e0b5ccd03ca4749b8f66f38608ccbcb415cbd130d02de5eda80d042b83bee90e", size = 4411340, upload-time = "2025-10-26T00:34:00.759Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c2/34bce068f6bfb4c2e7bb1187bb64a3f3be254702b158c4ad05eacc0055cf/psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:909de94de7dd4d6086098a5755562207114c9638ec42c52d84c8a440c45fe084", size = 3867815, upload-time = "2025-10-26T00:34:33.181Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a1/c647e01ab162e6bfa52380e23e486215e9d28ffd31e9cf3cb1e9ca59008b/psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:7130effd0517881f3a852eff98729d51034128f0737f64f0d1c7ea8343d77bd7", size = 3541756, upload-time = "2025-10-26T00:35:08.622Z" }, + { url = "https://files.pythonhosted.org/packages/6b/d0/795bdaa8c946a7b7126bf7ca8d4371eaaa613093e3ec341a0e50f52cbee2/psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:89b3c5201ca616d69ca0c3c0003ca18f7170a679c445c7e386ebfb4f29aa738e", size = 3587950, upload-time = "2025-10-26T00:35:41.183Z" }, + { url = "https://files.pythonhosted.org/packages/53/cf/10c3e95827a3ca8af332dfc471befec86e15a14dc83cee893c49a4910dad/psycopg_binary-3.2.12-cp314-cp314-win_amd64.whl", hash = "sha256:48a8e29f3e38fcf8d393b8fe460d83e39c107ad7e5e61cd3858a7569e0554a39", size = 3005787, upload-time = "2025-10-26T00:36:06.783Z" }, ] [[package]] name = "pwdlib" version = "0.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/a0/9daed437a6226f632a25d98d65d60ba02bdafa920c90dcb6454c611ead6c/pwdlib-0.2.1.tar.gz", hash = "sha256:9a1d8a8fa09a2f7ebf208265e55d7d008103cbdc82b9e4902ffdd1ade91add5e", size = 11699 } +sdist = { url = "https://files.pythonhosted.org/packages/82/a0/9daed437a6226f632a25d98d65d60ba02bdafa920c90dcb6454c611ead6c/pwdlib-0.2.1.tar.gz", hash = "sha256:9a1d8a8fa09a2f7ebf208265e55d7d008103cbdc82b9e4902ffdd1ade91add5e", size = 11699, upload-time = "2024-08-19T06:48:59.58Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/f3/0dae5078a486f0fdf4d4a1121e103bc42694a9da9bea7b0f2c63f29cfbd3/pwdlib-0.2.1-py3-none-any.whl", hash = "sha256:1823dc6f22eae472b540e889ecf57fd424051d6a4023ec0bcf7f0de2d9d7ef8c", size = 8082 }, + { url = "https://files.pythonhosted.org/packages/01/f3/0dae5078a486f0fdf4d4a1121e103bc42694a9da9bea7b0f2c63f29cfbd3/pwdlib-0.2.1-py3-none-any.whl", hash = "sha256:1823dc6f22eae472b540e889ecf57fd424051d6a4023ec0bcf7f0de2d9d7ef8c", size = 8082, upload-time = "2024-08-19T06:49:00.997Z" }, ] [package.optional-dependencies] @@ -1410,9 +1410,9 @@ bcrypt = [ name = "pyasn1" version = "0.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 }, + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, ] [[package]] @@ -1422,23 +1422,23 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892 } +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259 }, + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, ] [[package]] name = "pycparser" version = "2.23" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734 } +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140 }, + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, ] [[package]] name = "pydantic" -version = "2.11.10" +version = "2.12.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1446,9 +1446,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/54/ecab642b3bed45f7d5f59b38443dcb36ef50f85af192e6ece103dbfe9587/pydantic-2.11.10.tar.gz", hash = "sha256:dc280f0982fbda6c38fada4e476dc0a4f3aeaf9c6ad4c28df68a666ec3c61423", size = 788494 } +sdist = { url = "https://files.pythonhosted.org/packages/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/1f/73c53fcbfb0b5a78f91176df41945ca466e71e9d9d836e5c522abda39ee7/pydantic-2.11.10-py3-none-any.whl", hash = "sha256:802a655709d49bd004c31e865ef37da30b540786a46bfce02333e0e24b5fe29a", size = 444823 }, + { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" }, ] [package.optional-dependencies] @@ -1458,30 +1458,55 @@ email = [ [[package]] name = "pydantic-core" -version = "2.33.2" +version = "2.41.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 }, - { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 }, - { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 }, - { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 }, - { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 }, - { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 }, - { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 }, - { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 }, - { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 }, - { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 }, - { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 }, - { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, - { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, - { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, - { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 }, - { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 }, - { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, + { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, + { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, + { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, + { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, + { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, + { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, + { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, + { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, + { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, + { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, + { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, + { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, ] [[package]] @@ -1492,9 +1517,9 @@ dependencies = [ { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/10/fb64987804cde41bcc39d9cd757cd5f2bb5d97b389d81aa70238b14b8a7e/pydantic_extra_types-2.10.6.tar.gz", hash = "sha256:c63d70bf684366e6bbe1f4ee3957952ebe6973d41e7802aea0b770d06b116aeb", size = 141858 } +sdist = { url = "https://files.pythonhosted.org/packages/3a/10/fb64987804cde41bcc39d9cd757cd5f2bb5d97b389d81aa70238b14b8a7e/pydantic_extra_types-2.10.6.tar.gz", hash = "sha256:c63d70bf684366e6bbe1f4ee3957952ebe6973d41e7802aea0b770d06b116aeb", size = 141858, upload-time = "2025-10-08T13:47:49.483Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/04/5c918669096da8d1c9ec7bb716bd72e755526103a61bc5e76a3e4fb23b53/pydantic_extra_types-2.10.6-py3-none-any.whl", hash = "sha256:6106c448316d30abf721b5b9fecc65e983ef2614399a24142d689c7546cc246a", size = 40949 }, + { url = "https://files.pythonhosted.org/packages/93/04/5c918669096da8d1c9ec7bb716bd72e755526103a61bc5e76a3e4fb23b53/pydantic_extra_types-2.10.6-py3-none-any.whl", hash = "sha256:6106c448316d30abf721b5b9fecc65e983ef2614399a24142d689c7546cc246a", size = 40949, upload-time = "2025-10-08T13:47:48.268Z" }, ] [[package]] @@ -1506,9 +1531,9 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/c5/dbbc27b814c71676593d1c3f718e6cd7d4f00652cefa24b75f7aa3efb25e/pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180", size = 188394 } +sdist = { url = "https://files.pythonhosted.org/packages/20/c5/dbbc27b814c71676593d1c3f718e6cd7d4f00652cefa24b75f7aa3efb25e/pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180", size = 188394, upload-time = "2025-09-24T14:19:11.764Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/d6/887a1ff844e64aa823fb4905978d882a633cfe295c32eacad582b78a7d8b/pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c", size = 48608 }, + { url = "https://files.pythonhosted.org/packages/83/d6/887a1ff844e64aa823fb4905978d882a633cfe295c32eacad582b78a7d8b/pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c", size = 48608, upload-time = "2025-09-24T14:19:10.015Z" }, ] [[package]] @@ -1518,27 +1543,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyparsing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/dd/e0e6a4fb84c22050f6a9701ad9fd6a67ef82faa7ba97b97eb6fdc6b49b34/pydot-3.0.4.tar.gz", hash = "sha256:3ce88b2558f3808b0376f22bfa6c263909e1c3981e2a7b629b65b451eee4a25d", size = 168167 } +sdist = { url = "https://files.pythonhosted.org/packages/66/dd/e0e6a4fb84c22050f6a9701ad9fd6a67ef82faa7ba97b97eb6fdc6b49b34/pydot-3.0.4.tar.gz", hash = "sha256:3ce88b2558f3808b0376f22bfa6c263909e1c3981e2a7b629b65b451eee4a25d", size = 168167, upload-time = "2025-01-05T16:18:45.763Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/5f/1ebfd430df05c4f9e438dd3313c4456eab937d976f6ab8ce81a98f9fb381/pydot-3.0.4-py3-none-any.whl", hash = "sha256:bfa9c3fc0c44ba1d132adce131802d7df00429d1a79cc0346b0a5cd374dbe9c6", size = 35776 }, + { url = "https://files.pythonhosted.org/packages/b0/5f/1ebfd430df05c4f9e438dd3313c4456eab937d976f6ab8ce81a98f9fb381/pydot-3.0.4-py3-none-any.whl", hash = "sha256:bfa9c3fc0c44ba1d132adce131802d7df00429d1a79cc0346b0a5cd374dbe9c6", size = 35776, upload-time = "2025-01-05T16:18:42.836Z" }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] name = "pyjwt" version = "2.10.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785 } +sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997 }, + { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, ] [package.optional-dependencies] @@ -1550,18 +1575,18 @@ crypto = [ name = "pyparsing" version = "3.2.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274 } +sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274, upload-time = "2025-09-21T04:11:06.277Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890 }, + { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" }, ] [[package]] name = "pyreadline3" version = "3.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839 } +sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839, upload-time = "2024-09-19T02:40:10.062Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178 }, + { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" }, ] [[package]] @@ -1572,9 +1597,9 @@ dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/1b/0aa08ee42948b61745ac5b5b5ccaec4669e8884b53d31c8ec20b2fcd6b6f/pyright-1.1.407.tar.gz", hash = "sha256:099674dba5c10489832d4a4b2d302636152a9a42d317986c38474c76fe562262", size = 4122872 } +sdist = { url = "https://files.pythonhosted.org/packages/a6/1b/0aa08ee42948b61745ac5b5b5ccaec4669e8884b53d31c8ec20b2fcd6b6f/pyright-1.1.407.tar.gz", hash = "sha256:099674dba5c10489832d4a4b2d302636152a9a42d317986c38474c76fe562262", size = 4122872, upload-time = "2025-10-24T23:17:15.145Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/93/b69052907d032b00c40cb656d21438ec00b3a471733de137a3f65a49a0a0/pyright-1.1.407-py3-none-any.whl", hash = "sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21", size = 5997008 }, + { url = "https://files.pythonhosted.org/packages/dc/93/b69052907d032b00c40cb656d21438ec00b3a471733de137a3f65a49a0a0/pyright-1.1.407-py3-none-any.whl", hash = "sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21", size = 5997008, upload-time = "2025-10-24T23:17:13.159Z" }, ] [[package]] @@ -1588,9 +1613,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618 } +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750 }, + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, ] [[package]] @@ -1602,9 +1627,9 @@ dependencies = [ { name = "pytest" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f9/37/ad095d92242fe5c6b4b793191240375c01f6508960f31179de7f0e22cb96/pytest_alembic-0.12.1.tar.gz", hash = "sha256:4e2b477d93464d0cfe80487fdf63922bfd22f29153ca980c1bccf1dbf833cf12", size = 30635 } +sdist = { url = "https://files.pythonhosted.org/packages/f9/37/ad095d92242fe5c6b4b793191240375c01f6508960f31179de7f0e22cb96/pytest_alembic-0.12.1.tar.gz", hash = "sha256:4e2b477d93464d0cfe80487fdf63922bfd22f29153ca980c1bccf1dbf833cf12", size = 30635, upload-time = "2025-05-27T14:15:29.85Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/f4/ded73992f972360adf84781b7e58729a3778e4358d482e1fe375c83948b4/pytest_alembic-0.12.1-py3-none-any.whl", hash = "sha256:d0d6be79f1c597278fbeda08c5558e7b8770af099521b0aa164e0df4aed945da", size = 36571 }, + { url = "https://files.pythonhosted.org/packages/8b/f4/ded73992f972360adf84781b7e58729a3778e4358d482e1fe375c83948b4/pytest_alembic-0.12.1-py3-none-any.whl", hash = "sha256:d0d6be79f1c597278fbeda08c5558e7b8770af099521b0aa164e0df4aed945da", size = 36571, upload-time = "2025-05-27T14:15:28.817Z" }, ] [[package]] @@ -1614,9 +1639,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119 } +sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095 }, + { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, ] [[package]] @@ -1628,9 +1653,9 @@ dependencies = [ { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328 } +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424 }, + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, ] [[package]] @@ -1640,27 +1665,27 @@ 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 } +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 }, + { 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-dotenv" version = "1.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221 } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230 }, + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, ] [[package]] name = "python-multipart" version = "0.0.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, + { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, ] [[package]] @@ -1670,127 +1695,127 @@ 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 } +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 }, + { 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 = "pytz" version = "2025.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, ] [[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 } -wheels = [ - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 }, +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/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 = "redis" version = "7.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/8f/f125feec0b958e8d22c8f0b492b30b1991d9499a4315dfde466cf4289edc/redis-7.0.1.tar.gz", hash = "sha256:c949df947dca995dc68fdf5a7863950bf6df24f8d6022394585acc98e81624f1", size = 4755322 } +sdist = { url = "https://files.pythonhosted.org/packages/57/8f/f125feec0b958e8d22c8f0b492b30b1991d9499a4315dfde466cf4289edc/redis-7.0.1.tar.gz", hash = "sha256:c949df947dca995dc68fdf5a7863950bf6df24f8d6022394585acc98e81624f1", size = 4755322, upload-time = "2025-10-27T14:34:00.33Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/97/9f22a33c475cda519f20aba6babb340fb2f2254a02fb947816960d1e669a/redis-7.0.1-py3-none-any.whl", hash = "sha256:4977af3c7d67f8f0eb8b6fec0dafc9605db9343142f634041fb0235f67c0588a", size = 339938 }, + { url = "https://files.pythonhosted.org/packages/e9/97/9f22a33c475cda519f20aba6babb340fb2f2254a02fb947816960d1e669a/redis-7.0.1-py3-none-any.whl", hash = "sha256:4977af3c7d67f8f0eb8b6fec0dafc9605db9343142f634041fb0235f67c0588a", size = 339938, upload-time = "2025-10-27T14:33:58.553Z" }, ] [[package]] name = "regex" version = "2025.11.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/a9/546676f25e573a4cf00fe8e119b78a37b6a8fe2dc95cda877b30889c9c45/regex-2025.11.3.tar.gz", hash = "sha256:1fedc720f9bb2494ce31a58a1631f9c82df6a09b49c19517ea5cc280b4541e01", size = 414669 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/a7/dda24ebd49da46a197436ad96378f17df30ceb40e52e859fc42cac45b850/regex-2025.11.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c1e448051717a334891f2b9a620fe36776ebf3dd8ec46a0b877c8ae69575feb4", size = 489081 }, - { url = "https://files.pythonhosted.org/packages/19/22/af2dc751aacf88089836aa088a1a11c4f21a04707eb1b0478e8e8fb32847/regex-2025.11.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b5aca4d5dfd7fbfbfbdaf44850fcc7709a01146a797536a8f84952e940cca76", size = 291123 }, - { url = "https://files.pythonhosted.org/packages/a3/88/1a3ea5672f4b0a84802ee9891b86743438e7c04eb0b8f8c4e16a42375327/regex-2025.11.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:04d2765516395cf7dda331a244a3282c0f5ae96075f728629287dfa6f76ba70a", size = 288814 }, - { url = "https://files.pythonhosted.org/packages/fb/8c/f5987895bf42b8ddeea1b315c9fedcfe07cadee28b9c98cf50d00adcb14d/regex-2025.11.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d9903ca42bfeec4cebedba8022a7c97ad2aab22e09573ce9976ba01b65e4361", size = 798592 }, - { url = "https://files.pythonhosted.org/packages/99/2a/6591ebeede78203fa77ee46a1c36649e02df9eaa77a033d1ccdf2fcd5d4e/regex-2025.11.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:639431bdc89d6429f6721625e8129413980ccd62e9d3f496be618a41d205f160", size = 864122 }, - { url = "https://files.pythonhosted.org/packages/94/d6/be32a87cf28cf8ed064ff281cfbd49aefd90242a83e4b08b5a86b38e8eb4/regex-2025.11.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f117efad42068f9715677c8523ed2be1518116d1c49b1dd17987716695181efe", size = 912272 }, - { url = "https://files.pythonhosted.org/packages/62/11/9bcef2d1445665b180ac7f230406ad80671f0fc2a6ffb93493b5dd8cd64c/regex-2025.11.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4aecb6f461316adf9f1f0f6a4a1a3d79e045f9b71ec76055a791affa3b285850", size = 803497 }, - { url = "https://files.pythonhosted.org/packages/e5/a7/da0dc273d57f560399aa16d8a68ae7f9b57679476fc7ace46501d455fe84/regex-2025.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3b3a5f320136873cc5561098dfab677eea139521cb9a9e8db98b7e64aef44cbc", size = 787892 }, - { url = "https://files.pythonhosted.org/packages/da/4b/732a0c5a9736a0b8d6d720d4945a2f1e6f38f87f48f3173559f53e8d5d82/regex-2025.11.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:75fa6f0056e7efb1f42a1c34e58be24072cb9e61a601340cc1196ae92326a4f9", size = 858462 }, - { url = "https://files.pythonhosted.org/packages/0c/f5/a2a03df27dc4c2d0c769220f5110ba8c4084b0bfa9ab0f9b4fcfa3d2b0fc/regex-2025.11.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:dbe6095001465294f13f1adcd3311e50dd84e5a71525f20a10bd16689c61ce0b", size = 850528 }, - { url = "https://files.pythonhosted.org/packages/d6/09/e1cd5bee3841c7f6eb37d95ca91cdee7100b8f88b81e41c2ef426910891a/regex-2025.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:454d9b4ae7881afbc25015b8627c16d88a597479b9dea82b8c6e7e2e07240dc7", size = 789866 }, - { url = "https://files.pythonhosted.org/packages/eb/51/702f5ea74e2a9c13d855a6a85b7f80c30f9e72a95493260193c07f3f8d74/regex-2025.11.3-cp313-cp313-win32.whl", hash = "sha256:28ba4d69171fc6e9896337d4fc63a43660002b7da53fc15ac992abcf3410917c", size = 266189 }, - { url = "https://files.pythonhosted.org/packages/8b/00/6e29bb314e271a743170e53649db0fdb8e8ff0b64b4f425f5602f4eb9014/regex-2025.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:bac4200befe50c670c405dc33af26dad5a3b6b255dd6c000d92fe4629f9ed6a5", size = 277054 }, - { url = "https://files.pythonhosted.org/packages/25/f1/b156ff9f2ec9ac441710764dda95e4edaf5f36aca48246d1eea3f1fd96ec/regex-2025.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:2292cd5a90dab247f9abe892ac584cb24f0f54680c73fcb4a7493c66c2bf2467", size = 270325 }, - { url = "https://files.pythonhosted.org/packages/20/28/fd0c63357caefe5680b8ea052131acbd7f456893b69cc2a90cc3e0dc90d4/regex-2025.11.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:1eb1ebf6822b756c723e09f5186473d93236c06c579d2cc0671a722d2ab14281", size = 491984 }, - { url = "https://files.pythonhosted.org/packages/df/ec/7014c15626ab46b902b3bcc4b28a7bae46d8f281fc7ea9c95e22fcaaa917/regex-2025.11.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1e00ec2970aab10dc5db34af535f21fcf32b4a31d99e34963419636e2f85ae39", size = 292673 }, - { url = "https://files.pythonhosted.org/packages/23/ab/3b952ff7239f20d05f1f99e9e20188513905f218c81d52fb5e78d2bf7634/regex-2025.11.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a4cb042b615245d5ff9b3794f56be4138b5adc35a4166014d31d1814744148c7", size = 291029 }, - { url = "https://files.pythonhosted.org/packages/21/7e/3dc2749fc684f455f162dcafb8a187b559e2614f3826877d3844a131f37b/regex-2025.11.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44f264d4bf02f3176467d90b294d59bf1db9fe53c141ff772f27a8b456b2a9ed", size = 807437 }, - { url = "https://files.pythonhosted.org/packages/1b/0b/d529a85ab349c6a25d1ca783235b6e3eedf187247eab536797021f7126c6/regex-2025.11.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7be0277469bf3bd7a34a9c57c1b6a724532a0d235cd0dc4e7f4316f982c28b19", size = 873368 }, - { url = "https://files.pythonhosted.org/packages/7d/18/2d868155f8c9e3e9d8f9e10c64e9a9f496bb8f7e037a88a8bed26b435af6/regex-2025.11.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0d31e08426ff4b5b650f68839f5af51a92a5b51abd8554a60c2fbc7c71f25d0b", size = 914921 }, - { url = "https://files.pythonhosted.org/packages/2d/71/9d72ff0f354fa783fe2ba913c8734c3b433b86406117a8db4ea2bf1c7a2f/regex-2025.11.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e43586ce5bd28f9f285a6e729466841368c4a0353f6fd08d4ce4630843d3648a", size = 812708 }, - { url = "https://files.pythonhosted.org/packages/e7/19/ce4bf7f5575c97f82b6e804ffb5c4e940c62609ab2a0d9538d47a7fdf7d4/regex-2025.11.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0f9397d561a4c16829d4e6ff75202c1c08b68a3bdbfe29dbfcdb31c9830907c6", size = 795472 }, - { url = "https://files.pythonhosted.org/packages/03/86/fd1063a176ffb7b2315f9a1b08d17b18118b28d9df163132615b835a26ee/regex-2025.11.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:dd16e78eb18ffdb25ee33a0682d17912e8cc8a770e885aeee95020046128f1ce", size = 868341 }, - { url = "https://files.pythonhosted.org/packages/12/43/103fb2e9811205e7386366501bc866a164a0430c79dd59eac886a2822950/regex-2025.11.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:ffcca5b9efe948ba0661e9df0fa50d2bc4b097c70b9810212d6b62f05d83b2dd", size = 854666 }, - { url = "https://files.pythonhosted.org/packages/7d/22/e392e53f3869b75804762c7c848bd2dd2abf2b70fb0e526f58724638bd35/regex-2025.11.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c56b4d162ca2b43318ac671c65bd4d563e841a694ac70e1a976ac38fcf4ca1d2", size = 799473 }, - { url = "https://files.pythonhosted.org/packages/4f/f9/8bd6b656592f925b6845fcbb4d57603a3ac2fb2373344ffa1ed70aa6820a/regex-2025.11.3-cp313-cp313t-win32.whl", hash = "sha256:9ddc42e68114e161e51e272f667d640f97e84a2b9ef14b7477c53aac20c2d59a", size = 268792 }, - { url = "https://files.pythonhosted.org/packages/e5/87/0e7d603467775ff65cd2aeabf1b5b50cc1c3708556a8b849a2fa4dd1542b/regex-2025.11.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7a7c7fdf755032ffdd72c77e3d8096bdcb0eb92e89e17571a196f03d88b11b3c", size = 280214 }, - { url = "https://files.pythonhosted.org/packages/8d/d0/2afc6f8e94e2b64bfb738a7c2b6387ac1699f09f032d363ed9447fd2bb57/regex-2025.11.3-cp313-cp313t-win_arm64.whl", hash = "sha256:df9eb838c44f570283712e7cff14c16329a9f0fb19ca492d21d4b7528ee6821e", size = 271469 }, - { url = "https://files.pythonhosted.org/packages/31/e9/f6e13de7e0983837f7b6d238ad9458800a874bf37c264f7923e63409944c/regex-2025.11.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9697a52e57576c83139d7c6f213d64485d3df5bf84807c35fa409e6c970801c6", size = 489089 }, - { url = "https://files.pythonhosted.org/packages/a3/5c/261f4a262f1fa65141c1b74b255988bd2fa020cc599e53b080667d591cfc/regex-2025.11.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e18bc3f73bd41243c9b38a6d9f2366cd0e0137a9aebe2d8ff76c5b67d4c0a3f4", size = 291059 }, - { url = "https://files.pythonhosted.org/packages/8e/57/f14eeb7f072b0e9a5a090d1712741fd8f214ec193dba773cf5410108bb7d/regex-2025.11.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:61a08bcb0ec14ff4e0ed2044aad948d0659604f824cbd50b55e30b0ec6f09c73", size = 288900 }, - { url = "https://files.pythonhosted.org/packages/3c/6b/1d650c45e99a9b327586739d926a1cd4e94666b1bd4af90428b36af66dc7/regex-2025.11.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9c30003b9347c24bcc210958c5d167b9e4f9be786cb380a7d32f14f9b84674f", size = 799010 }, - { url = "https://files.pythonhosted.org/packages/99/ee/d66dcbc6b628ce4e3f7f0cbbb84603aa2fc0ffc878babc857726b8aab2e9/regex-2025.11.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4e1e592789704459900728d88d41a46fe3969b82ab62945560a31732ffc19a6d", size = 864893 }, - { url = "https://files.pythonhosted.org/packages/bf/2d/f238229f1caba7ac87a6c4153d79947fb0261415827ae0f77c304260c7d3/regex-2025.11.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6538241f45eb5a25aa575dbba1069ad786f68a4f2773a29a2bd3dd1f9de787be", size = 911522 }, - { url = "https://files.pythonhosted.org/packages/bd/3d/22a4eaba214a917c80e04f6025d26143690f0419511e0116508e24b11c9b/regex-2025.11.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce22519c989bb72a7e6b36a199384c53db7722fe669ba891da75907fe3587db", size = 803272 }, - { url = "https://files.pythonhosted.org/packages/84/b1/03188f634a409353a84b5ef49754b97dbcc0c0f6fd6c8ede505a8960a0a4/regex-2025.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:66d559b21d3640203ab9075797a55165d79017520685fb407b9234d72ab63c62", size = 787958 }, - { url = "https://files.pythonhosted.org/packages/99/6a/27d072f7fbf6fadd59c64d210305e1ff865cc3b78b526fd147db768c553b/regex-2025.11.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:669dcfb2e38f9e8c69507bace46f4889e3abbfd9b0c29719202883c0a603598f", size = 859289 }, - { url = "https://files.pythonhosted.org/packages/9a/70/1b3878f648e0b6abe023172dacb02157e685564853cc363d9961bcccde4e/regex-2025.11.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:32f74f35ff0f25a5021373ac61442edcb150731fbaa28286bbc8bb1582c89d02", size = 850026 }, - { url = "https://files.pythonhosted.org/packages/dd/d5/68e25559b526b8baab8e66839304ede68ff6727237a47727d240006bd0ff/regex-2025.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e6c7a21dffba883234baefe91bc3388e629779582038f75d2a5be918e250f0ed", size = 789499 }, - { url = "https://files.pythonhosted.org/packages/fc/df/43971264857140a350910d4e33df725e8c94dd9dee8d2e4729fa0d63d49e/regex-2025.11.3-cp314-cp314-win32.whl", hash = "sha256:795ea137b1d809eb6836b43748b12634291c0ed55ad50a7d72d21edf1cd565c4", size = 271604 }, - { url = "https://files.pythonhosted.org/packages/01/6f/9711b57dc6894a55faf80a4c1b5aa4f8649805cb9c7aef46f7d27e2b9206/regex-2025.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:9f95fbaa0ee1610ec0fc6b26668e9917a582ba80c52cc6d9ada15e30aa9ab9ad", size = 280320 }, - { url = "https://files.pythonhosted.org/packages/f1/7e/f6eaa207d4377481f5e1775cdeb5a443b5a59b392d0065f3417d31d80f87/regex-2025.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:dfec44d532be4c07088c3de2876130ff0fbeeacaa89a137decbbb5f665855a0f", size = 273372 }, - { url = "https://files.pythonhosted.org/packages/c3/06/49b198550ee0f5e4184271cee87ba4dfd9692c91ec55289e6282f0f86ccf/regex-2025.11.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ba0d8a5d7f04f73ee7d01d974d47c5834f8a1b0224390e4fe7c12a3a92a78ecc", size = 491985 }, - { url = "https://files.pythonhosted.org/packages/ce/bf/abdafade008f0b1c9da10d934034cb670432d6cf6cbe38bbb53a1cfd6cf8/regex-2025.11.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:442d86cf1cfe4faabf97db7d901ef58347efd004934da045c745e7b5bd57ac49", size = 292669 }, - { url = "https://files.pythonhosted.org/packages/f9/ef/0c357bb8edbd2ad8e273fcb9e1761bc37b8acbc6e1be050bebd6475f19c1/regex-2025.11.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fd0a5e563c756de210bb964789b5abe4f114dacae9104a47e1a649b910361536", size = 291030 }, - { url = "https://files.pythonhosted.org/packages/79/06/edbb67257596649b8fb088d6aeacbcb248ac195714b18a65e018bf4c0b50/regex-2025.11.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf3490bcbb985a1ae97b2ce9ad1c0f06a852d5b19dde9b07bdf25bf224248c95", size = 807674 }, - { url = "https://files.pythonhosted.org/packages/f4/d9/ad4deccfce0ea336296bd087f1a191543bb99ee1c53093dcd4c64d951d00/regex-2025.11.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3809988f0a8b8c9dcc0f92478d6501fac7200b9ec56aecf0ec21f4a2ec4b6009", size = 873451 }, - { url = "https://files.pythonhosted.org/packages/13/75/a55a4724c56ef13e3e04acaab29df26582f6978c000ac9cd6810ad1f341f/regex-2025.11.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f4ff94e58e84aedb9c9fce66d4ef9f27a190285b451420f297c9a09f2b9abee9", size = 914980 }, - { url = "https://files.pythonhosted.org/packages/67/1e/a1657ee15bd9116f70d4a530c736983eed997b361e20ecd8f5ca3759d5c5/regex-2025.11.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eb542fd347ce61e1321b0a6b945d5701528dca0cd9759c2e3bb8bd57e47964d", size = 812852 }, - { url = "https://files.pythonhosted.org/packages/b8/6f/f7516dde5506a588a561d296b2d0044839de06035bb486b326065b4c101e/regex-2025.11.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2d5919075a1f2e413c00b056ea0c2f065b3f5fe83c3d07d325ab92dce51d6", size = 795566 }, - { url = "https://files.pythonhosted.org/packages/d9/dd/3d10b9e170cc16fb34cb2cef91513cf3df65f440b3366030631b2984a264/regex-2025.11.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3f8bf11a4827cc7ce5a53d4ef6cddd5ad25595d3c1435ef08f76825851343154", size = 868463 }, - { url = "https://files.pythonhosted.org/packages/f5/8e/935e6beff1695aa9085ff83195daccd72acc82c81793df480f34569330de/regex-2025.11.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:22c12d837298651e5550ac1d964e4ff57c3f56965fc1812c90c9fb2028eaf267", size = 854694 }, - { url = "https://files.pythonhosted.org/packages/92/12/10650181a040978b2f5720a6a74d44f841371a3d984c2083fc1752e4acf6/regex-2025.11.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:62ba394a3dda9ad41c7c780f60f6e4a70988741415ae96f6d1bf6c239cf01379", size = 799691 }, - { url = "https://files.pythonhosted.org/packages/67/90/8f37138181c9a7690e7e4cb388debbd389342db3c7381d636d2875940752/regex-2025.11.3-cp314-cp314t-win32.whl", hash = "sha256:4bf146dca15cdd53224a1bf46d628bd7590e4a07fbb69e720d561aea43a32b38", size = 274583 }, - { url = "https://files.pythonhosted.org/packages/8f/cd/867f5ec442d56beb56f5f854f40abcfc75e11d10b11fdb1869dd39c63aaf/regex-2025.11.3-cp314-cp314t-win_amd64.whl", hash = "sha256:adad1a1bcf1c9e76346e091d22d23ac54ef28e1365117d99521631078dfec9de", size = 284286 }, - { url = "https://files.pythonhosted.org/packages/20/31/32c0c4610cbc070362bf1d2e4ea86d1ea29014d400a6d6c2486fcfd57766/regex-2025.11.3-cp314-cp314t-win_arm64.whl", hash = "sha256:c54f768482cef41e219720013cd05933b6f971d9562544d691c68699bf2b6801", size = 274741 }, +sdist = { url = "https://files.pythonhosted.org/packages/cc/a9/546676f25e573a4cf00fe8e119b78a37b6a8fe2dc95cda877b30889c9c45/regex-2025.11.3.tar.gz", hash = "sha256:1fedc720f9bb2494ce31a58a1631f9c82df6a09b49c19517ea5cc280b4541e01", size = 414669, upload-time = "2025-11-03T21:34:22.089Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/a7/dda24ebd49da46a197436ad96378f17df30ceb40e52e859fc42cac45b850/regex-2025.11.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c1e448051717a334891f2b9a620fe36776ebf3dd8ec46a0b877c8ae69575feb4", size = 489081, upload-time = "2025-11-03T21:31:55.9Z" }, + { url = "https://files.pythonhosted.org/packages/19/22/af2dc751aacf88089836aa088a1a11c4f21a04707eb1b0478e8e8fb32847/regex-2025.11.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b5aca4d5dfd7fbfbfbdaf44850fcc7709a01146a797536a8f84952e940cca76", size = 291123, upload-time = "2025-11-03T21:31:57.758Z" }, + { url = "https://files.pythonhosted.org/packages/a3/88/1a3ea5672f4b0a84802ee9891b86743438e7c04eb0b8f8c4e16a42375327/regex-2025.11.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:04d2765516395cf7dda331a244a3282c0f5ae96075f728629287dfa6f76ba70a", size = 288814, upload-time = "2025-11-03T21:32:01.12Z" }, + { url = "https://files.pythonhosted.org/packages/fb/8c/f5987895bf42b8ddeea1b315c9fedcfe07cadee28b9c98cf50d00adcb14d/regex-2025.11.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d9903ca42bfeec4cebedba8022a7c97ad2aab22e09573ce9976ba01b65e4361", size = 798592, upload-time = "2025-11-03T21:32:03.006Z" }, + { url = "https://files.pythonhosted.org/packages/99/2a/6591ebeede78203fa77ee46a1c36649e02df9eaa77a033d1ccdf2fcd5d4e/regex-2025.11.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:639431bdc89d6429f6721625e8129413980ccd62e9d3f496be618a41d205f160", size = 864122, upload-time = "2025-11-03T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/94/d6/be32a87cf28cf8ed064ff281cfbd49aefd90242a83e4b08b5a86b38e8eb4/regex-2025.11.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f117efad42068f9715677c8523ed2be1518116d1c49b1dd17987716695181efe", size = 912272, upload-time = "2025-11-03T21:32:06.148Z" }, + { url = "https://files.pythonhosted.org/packages/62/11/9bcef2d1445665b180ac7f230406ad80671f0fc2a6ffb93493b5dd8cd64c/regex-2025.11.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4aecb6f461316adf9f1f0f6a4a1a3d79e045f9b71ec76055a791affa3b285850", size = 803497, upload-time = "2025-11-03T21:32:08.162Z" }, + { url = "https://files.pythonhosted.org/packages/e5/a7/da0dc273d57f560399aa16d8a68ae7f9b57679476fc7ace46501d455fe84/regex-2025.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3b3a5f320136873cc5561098dfab677eea139521cb9a9e8db98b7e64aef44cbc", size = 787892, upload-time = "2025-11-03T21:32:09.769Z" }, + { url = "https://files.pythonhosted.org/packages/da/4b/732a0c5a9736a0b8d6d720d4945a2f1e6f38f87f48f3173559f53e8d5d82/regex-2025.11.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:75fa6f0056e7efb1f42a1c34e58be24072cb9e61a601340cc1196ae92326a4f9", size = 858462, upload-time = "2025-11-03T21:32:11.769Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f5/a2a03df27dc4c2d0c769220f5110ba8c4084b0bfa9ab0f9b4fcfa3d2b0fc/regex-2025.11.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:dbe6095001465294f13f1adcd3311e50dd84e5a71525f20a10bd16689c61ce0b", size = 850528, upload-time = "2025-11-03T21:32:13.906Z" }, + { url = "https://files.pythonhosted.org/packages/d6/09/e1cd5bee3841c7f6eb37d95ca91cdee7100b8f88b81e41c2ef426910891a/regex-2025.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:454d9b4ae7881afbc25015b8627c16d88a597479b9dea82b8c6e7e2e07240dc7", size = 789866, upload-time = "2025-11-03T21:32:15.748Z" }, + { url = "https://files.pythonhosted.org/packages/eb/51/702f5ea74e2a9c13d855a6a85b7f80c30f9e72a95493260193c07f3f8d74/regex-2025.11.3-cp313-cp313-win32.whl", hash = "sha256:28ba4d69171fc6e9896337d4fc63a43660002b7da53fc15ac992abcf3410917c", size = 266189, upload-time = "2025-11-03T21:32:17.493Z" }, + { url = "https://files.pythonhosted.org/packages/8b/00/6e29bb314e271a743170e53649db0fdb8e8ff0b64b4f425f5602f4eb9014/regex-2025.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:bac4200befe50c670c405dc33af26dad5a3b6b255dd6c000d92fe4629f9ed6a5", size = 277054, upload-time = "2025-11-03T21:32:19.042Z" }, + { url = "https://files.pythonhosted.org/packages/25/f1/b156ff9f2ec9ac441710764dda95e4edaf5f36aca48246d1eea3f1fd96ec/regex-2025.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:2292cd5a90dab247f9abe892ac584cb24f0f54680c73fcb4a7493c66c2bf2467", size = 270325, upload-time = "2025-11-03T21:32:21.338Z" }, + { url = "https://files.pythonhosted.org/packages/20/28/fd0c63357caefe5680b8ea052131acbd7f456893b69cc2a90cc3e0dc90d4/regex-2025.11.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:1eb1ebf6822b756c723e09f5186473d93236c06c579d2cc0671a722d2ab14281", size = 491984, upload-time = "2025-11-03T21:32:23.466Z" }, + { url = "https://files.pythonhosted.org/packages/df/ec/7014c15626ab46b902b3bcc4b28a7bae46d8f281fc7ea9c95e22fcaaa917/regex-2025.11.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1e00ec2970aab10dc5db34af535f21fcf32b4a31d99e34963419636e2f85ae39", size = 292673, upload-time = "2025-11-03T21:32:25.034Z" }, + { url = "https://files.pythonhosted.org/packages/23/ab/3b952ff7239f20d05f1f99e9e20188513905f218c81d52fb5e78d2bf7634/regex-2025.11.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a4cb042b615245d5ff9b3794f56be4138b5adc35a4166014d31d1814744148c7", size = 291029, upload-time = "2025-11-03T21:32:26.528Z" }, + { url = "https://files.pythonhosted.org/packages/21/7e/3dc2749fc684f455f162dcafb8a187b559e2614f3826877d3844a131f37b/regex-2025.11.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44f264d4bf02f3176467d90b294d59bf1db9fe53c141ff772f27a8b456b2a9ed", size = 807437, upload-time = "2025-11-03T21:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/1b/0b/d529a85ab349c6a25d1ca783235b6e3eedf187247eab536797021f7126c6/regex-2025.11.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7be0277469bf3bd7a34a9c57c1b6a724532a0d235cd0dc4e7f4316f982c28b19", size = 873368, upload-time = "2025-11-03T21:32:30.4Z" }, + { url = "https://files.pythonhosted.org/packages/7d/18/2d868155f8c9e3e9d8f9e10c64e9a9f496bb8f7e037a88a8bed26b435af6/regex-2025.11.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0d31e08426ff4b5b650f68839f5af51a92a5b51abd8554a60c2fbc7c71f25d0b", size = 914921, upload-time = "2025-11-03T21:32:32.123Z" }, + { url = "https://files.pythonhosted.org/packages/2d/71/9d72ff0f354fa783fe2ba913c8734c3b433b86406117a8db4ea2bf1c7a2f/regex-2025.11.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e43586ce5bd28f9f285a6e729466841368c4a0353f6fd08d4ce4630843d3648a", size = 812708, upload-time = "2025-11-03T21:32:34.305Z" }, + { url = "https://files.pythonhosted.org/packages/e7/19/ce4bf7f5575c97f82b6e804ffb5c4e940c62609ab2a0d9538d47a7fdf7d4/regex-2025.11.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0f9397d561a4c16829d4e6ff75202c1c08b68a3bdbfe29dbfcdb31c9830907c6", size = 795472, upload-time = "2025-11-03T21:32:36.364Z" }, + { url = "https://files.pythonhosted.org/packages/03/86/fd1063a176ffb7b2315f9a1b08d17b18118b28d9df163132615b835a26ee/regex-2025.11.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:dd16e78eb18ffdb25ee33a0682d17912e8cc8a770e885aeee95020046128f1ce", size = 868341, upload-time = "2025-11-03T21:32:38.042Z" }, + { url = "https://files.pythonhosted.org/packages/12/43/103fb2e9811205e7386366501bc866a164a0430c79dd59eac886a2822950/regex-2025.11.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:ffcca5b9efe948ba0661e9df0fa50d2bc4b097c70b9810212d6b62f05d83b2dd", size = 854666, upload-time = "2025-11-03T21:32:40.079Z" }, + { url = "https://files.pythonhosted.org/packages/7d/22/e392e53f3869b75804762c7c848bd2dd2abf2b70fb0e526f58724638bd35/regex-2025.11.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c56b4d162ca2b43318ac671c65bd4d563e841a694ac70e1a976ac38fcf4ca1d2", size = 799473, upload-time = "2025-11-03T21:32:42.148Z" }, + { url = "https://files.pythonhosted.org/packages/4f/f9/8bd6b656592f925b6845fcbb4d57603a3ac2fb2373344ffa1ed70aa6820a/regex-2025.11.3-cp313-cp313t-win32.whl", hash = "sha256:9ddc42e68114e161e51e272f667d640f97e84a2b9ef14b7477c53aac20c2d59a", size = 268792, upload-time = "2025-11-03T21:32:44.13Z" }, + { url = "https://files.pythonhosted.org/packages/e5/87/0e7d603467775ff65cd2aeabf1b5b50cc1c3708556a8b849a2fa4dd1542b/regex-2025.11.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7a7c7fdf755032ffdd72c77e3d8096bdcb0eb92e89e17571a196f03d88b11b3c", size = 280214, upload-time = "2025-11-03T21:32:45.853Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d0/2afc6f8e94e2b64bfb738a7c2b6387ac1699f09f032d363ed9447fd2bb57/regex-2025.11.3-cp313-cp313t-win_arm64.whl", hash = "sha256:df9eb838c44f570283712e7cff14c16329a9f0fb19ca492d21d4b7528ee6821e", size = 271469, upload-time = "2025-11-03T21:32:48.026Z" }, + { url = "https://files.pythonhosted.org/packages/31/e9/f6e13de7e0983837f7b6d238ad9458800a874bf37c264f7923e63409944c/regex-2025.11.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9697a52e57576c83139d7c6f213d64485d3df5bf84807c35fa409e6c970801c6", size = 489089, upload-time = "2025-11-03T21:32:50.027Z" }, + { url = "https://files.pythonhosted.org/packages/a3/5c/261f4a262f1fa65141c1b74b255988bd2fa020cc599e53b080667d591cfc/regex-2025.11.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e18bc3f73bd41243c9b38a6d9f2366cd0e0137a9aebe2d8ff76c5b67d4c0a3f4", size = 291059, upload-time = "2025-11-03T21:32:51.682Z" }, + { url = "https://files.pythonhosted.org/packages/8e/57/f14eeb7f072b0e9a5a090d1712741fd8f214ec193dba773cf5410108bb7d/regex-2025.11.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:61a08bcb0ec14ff4e0ed2044aad948d0659604f824cbd50b55e30b0ec6f09c73", size = 288900, upload-time = "2025-11-03T21:32:53.569Z" }, + { url = "https://files.pythonhosted.org/packages/3c/6b/1d650c45e99a9b327586739d926a1cd4e94666b1bd4af90428b36af66dc7/regex-2025.11.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9c30003b9347c24bcc210958c5d167b9e4f9be786cb380a7d32f14f9b84674f", size = 799010, upload-time = "2025-11-03T21:32:55.222Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/d66dcbc6b628ce4e3f7f0cbbb84603aa2fc0ffc878babc857726b8aab2e9/regex-2025.11.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4e1e592789704459900728d88d41a46fe3969b82ab62945560a31732ffc19a6d", size = 864893, upload-time = "2025-11-03T21:32:57.239Z" }, + { url = "https://files.pythonhosted.org/packages/bf/2d/f238229f1caba7ac87a6c4153d79947fb0261415827ae0f77c304260c7d3/regex-2025.11.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6538241f45eb5a25aa575dbba1069ad786f68a4f2773a29a2bd3dd1f9de787be", size = 911522, upload-time = "2025-11-03T21:32:59.274Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3d/22a4eaba214a917c80e04f6025d26143690f0419511e0116508e24b11c9b/regex-2025.11.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce22519c989bb72a7e6b36a199384c53db7722fe669ba891da75907fe3587db", size = 803272, upload-time = "2025-11-03T21:33:01.393Z" }, + { url = "https://files.pythonhosted.org/packages/84/b1/03188f634a409353a84b5ef49754b97dbcc0c0f6fd6c8ede505a8960a0a4/regex-2025.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:66d559b21d3640203ab9075797a55165d79017520685fb407b9234d72ab63c62", size = 787958, upload-time = "2025-11-03T21:33:03.379Z" }, + { url = "https://files.pythonhosted.org/packages/99/6a/27d072f7fbf6fadd59c64d210305e1ff865cc3b78b526fd147db768c553b/regex-2025.11.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:669dcfb2e38f9e8c69507bace46f4889e3abbfd9b0c29719202883c0a603598f", size = 859289, upload-time = "2025-11-03T21:33:05.374Z" }, + { url = "https://files.pythonhosted.org/packages/9a/70/1b3878f648e0b6abe023172dacb02157e685564853cc363d9961bcccde4e/regex-2025.11.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:32f74f35ff0f25a5021373ac61442edcb150731fbaa28286bbc8bb1582c89d02", size = 850026, upload-time = "2025-11-03T21:33:07.131Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d5/68e25559b526b8baab8e66839304ede68ff6727237a47727d240006bd0ff/regex-2025.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e6c7a21dffba883234baefe91bc3388e629779582038f75d2a5be918e250f0ed", size = 789499, upload-time = "2025-11-03T21:33:09.141Z" }, + { url = "https://files.pythonhosted.org/packages/fc/df/43971264857140a350910d4e33df725e8c94dd9dee8d2e4729fa0d63d49e/regex-2025.11.3-cp314-cp314-win32.whl", hash = "sha256:795ea137b1d809eb6836b43748b12634291c0ed55ad50a7d72d21edf1cd565c4", size = 271604, upload-time = "2025-11-03T21:33:10.9Z" }, + { url = "https://files.pythonhosted.org/packages/01/6f/9711b57dc6894a55faf80a4c1b5aa4f8649805cb9c7aef46f7d27e2b9206/regex-2025.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:9f95fbaa0ee1610ec0fc6b26668e9917a582ba80c52cc6d9ada15e30aa9ab9ad", size = 280320, upload-time = "2025-11-03T21:33:12.572Z" }, + { url = "https://files.pythonhosted.org/packages/f1/7e/f6eaa207d4377481f5e1775cdeb5a443b5a59b392d0065f3417d31d80f87/regex-2025.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:dfec44d532be4c07088c3de2876130ff0fbeeacaa89a137decbbb5f665855a0f", size = 273372, upload-time = "2025-11-03T21:33:14.219Z" }, + { url = "https://files.pythonhosted.org/packages/c3/06/49b198550ee0f5e4184271cee87ba4dfd9692c91ec55289e6282f0f86ccf/regex-2025.11.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ba0d8a5d7f04f73ee7d01d974d47c5834f8a1b0224390e4fe7c12a3a92a78ecc", size = 491985, upload-time = "2025-11-03T21:33:16.555Z" }, + { url = "https://files.pythonhosted.org/packages/ce/bf/abdafade008f0b1c9da10d934034cb670432d6cf6cbe38bbb53a1cfd6cf8/regex-2025.11.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:442d86cf1cfe4faabf97db7d901ef58347efd004934da045c745e7b5bd57ac49", size = 292669, upload-time = "2025-11-03T21:33:18.32Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ef/0c357bb8edbd2ad8e273fcb9e1761bc37b8acbc6e1be050bebd6475f19c1/regex-2025.11.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fd0a5e563c756de210bb964789b5abe4f114dacae9104a47e1a649b910361536", size = 291030, upload-time = "2025-11-03T21:33:20.048Z" }, + { url = "https://files.pythonhosted.org/packages/79/06/edbb67257596649b8fb088d6aeacbcb248ac195714b18a65e018bf4c0b50/regex-2025.11.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf3490bcbb985a1ae97b2ce9ad1c0f06a852d5b19dde9b07bdf25bf224248c95", size = 807674, upload-time = "2025-11-03T21:33:21.797Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d9/ad4deccfce0ea336296bd087f1a191543bb99ee1c53093dcd4c64d951d00/regex-2025.11.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3809988f0a8b8c9dcc0f92478d6501fac7200b9ec56aecf0ec21f4a2ec4b6009", size = 873451, upload-time = "2025-11-03T21:33:23.741Z" }, + { url = "https://files.pythonhosted.org/packages/13/75/a55a4724c56ef13e3e04acaab29df26582f6978c000ac9cd6810ad1f341f/regex-2025.11.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f4ff94e58e84aedb9c9fce66d4ef9f27a190285b451420f297c9a09f2b9abee9", size = 914980, upload-time = "2025-11-03T21:33:25.999Z" }, + { url = "https://files.pythonhosted.org/packages/67/1e/a1657ee15bd9116f70d4a530c736983eed997b361e20ecd8f5ca3759d5c5/regex-2025.11.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eb542fd347ce61e1321b0a6b945d5701528dca0cd9759c2e3bb8bd57e47964d", size = 812852, upload-time = "2025-11-03T21:33:27.852Z" }, + { url = "https://files.pythonhosted.org/packages/b8/6f/f7516dde5506a588a561d296b2d0044839de06035bb486b326065b4c101e/regex-2025.11.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2d5919075a1f2e413c00b056ea0c2f065b3f5fe83c3d07d325ab92dce51d6", size = 795566, upload-time = "2025-11-03T21:33:32.364Z" }, + { url = "https://files.pythonhosted.org/packages/d9/dd/3d10b9e170cc16fb34cb2cef91513cf3df65f440b3366030631b2984a264/regex-2025.11.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3f8bf11a4827cc7ce5a53d4ef6cddd5ad25595d3c1435ef08f76825851343154", size = 868463, upload-time = "2025-11-03T21:33:34.459Z" }, + { url = "https://files.pythonhosted.org/packages/f5/8e/935e6beff1695aa9085ff83195daccd72acc82c81793df480f34569330de/regex-2025.11.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:22c12d837298651e5550ac1d964e4ff57c3f56965fc1812c90c9fb2028eaf267", size = 854694, upload-time = "2025-11-03T21:33:36.793Z" }, + { url = "https://files.pythonhosted.org/packages/92/12/10650181a040978b2f5720a6a74d44f841371a3d984c2083fc1752e4acf6/regex-2025.11.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:62ba394a3dda9ad41c7c780f60f6e4a70988741415ae96f6d1bf6c239cf01379", size = 799691, upload-time = "2025-11-03T21:33:39.079Z" }, + { url = "https://files.pythonhosted.org/packages/67/90/8f37138181c9a7690e7e4cb388debbd389342db3c7381d636d2875940752/regex-2025.11.3-cp314-cp314t-win32.whl", hash = "sha256:4bf146dca15cdd53224a1bf46d628bd7590e4a07fbb69e720d561aea43a32b38", size = 274583, upload-time = "2025-11-03T21:33:41.302Z" }, + { url = "https://files.pythonhosted.org/packages/8f/cd/867f5ec442d56beb56f5f854f40abcfc75e11d10b11fdb1869dd39c63aaf/regex-2025.11.3-cp314-cp314t-win_amd64.whl", hash = "sha256:adad1a1bcf1c9e76346e091d22d23ac54ef28e1365117d99521631078dfec9de", size = 284286, upload-time = "2025-11-03T21:33:43.324Z" }, + { url = "https://files.pythonhosted.org/packages/20/31/32c0c4610cbc070362bf1d2e4ea86d1ea29014d400a6d6c2486fcfd57766/regex-2025.11.3-cp314-cp314t-win_arm64.whl", hash = "sha256:c54f768482cef41e219720013cd05933b6f971d9562544d691c68699bf2b6801", size = 274741, upload-time = "2025-11-03T21:33:45.557Z" }, ] [[package]] @@ -1862,7 +1887,7 @@ requires-dist = [ { name = "email-validator", specifier = ">=2.2.0" }, { name = "fastapi", extras = ["standard"], specifier = ">=0.115.14" }, { name = "fastapi-filter", specifier = ">=2.0.1" }, - { name = "fastapi-mail", git = "https://github.com/simonvanlierde/fastapi-mail?rev=f32147ec1a450ed22262913c5ac7ec3b67dd0117" }, + { name = "fastapi-mail", git = "https://github.com/simonvanlierde/fastapi-mail?rev=6c6f04a7afaf3cdced82764009a2f1f2a3c3ee6c" }, { name = "fastapi-pagination", specifier = ">=0.13.2" }, { name = "fastapi-storages", specifier = ">=0.3.0" }, { name = "fastapi-users", extras = ["oauth", "sqlalchemy"], specifier = ">=14.0.1" }, @@ -1871,7 +1896,7 @@ requires-dist = [ { name = "mjml", specifier = ">=0.11.1" }, { name = "pillow", specifier = ">=11.2.1" }, { name = "psycopg", extras = ["binary"], specifier = ">=3.2.9" }, - { name = "pydantic", specifier = ">=2.11,<2.12" }, + { name = "pydantic", specifier = ">=2.12" }, { name = "pydantic-extra-types", specifier = ">=2.10.5" }, { name = "pydantic-settings", specifier = ">=2.10.1" }, { name = "python-dotenv", specifier = ">=1.1.1" }, @@ -1879,7 +1904,7 @@ requires-dist = [ { name = "redis", specifier = ">=5.2.1" }, { name = "relab-rpi-cam-models", specifier = ">=0.1.1" }, { name = "sqlalchemy", specifier = ">=2.0.41" }, - { name = "sqlmodel", specifier = ">=0.0.24" }, + { name = "sqlmodel", specifier = ">=0.0.27" }, { name = "tldextract", specifier = ">=5.3.0" }, ] @@ -1920,9 +1945,9 @@ dependencies = [ { name = "pillow" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/55/1bd336b19e2483bea7d1f8c2967d25a297ddacb686ee72c13b2023ae97d0/relab_rpi_cam_models-0.1.1.tar.gz", hash = "sha256:6ac60f787b33c7951edd956c78c939764af48e63eaa9809eaa3590a604cf1dde", size = 3943 } +sdist = { url = "https://files.pythonhosted.org/packages/58/55/1bd336b19e2483bea7d1f8c2967d25a297ddacb686ee72c13b2023ae97d0/relab_rpi_cam_models-0.1.1.tar.gz", hash = "sha256:6ac60f787b33c7951edd956c78c939764af48e63eaa9809eaa3590a604cf1dde", size = 3943, upload-time = "2025-08-19T23:38:05.058Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/85/1ec6ec0c444dcccdc343b2978d5bcde8043b0b696c6d853c51e115d7dc53/relab_rpi_cam_models-0.1.1-py3-none-any.whl", hash = "sha256:bba3182febdbbc8f48897e1dc42ac2b779a48de8ef6be867b6131eed2b019f6b", size = 5552 }, + { url = "https://files.pythonhosted.org/packages/8f/85/1ec6ec0c444dcccdc343b2978d5bcde8043b0b696c6d853c51e115d7dc53/relab_rpi_cam_models-0.1.1-py3-none-any.whl", hash = "sha256:bba3182febdbbc8f48897e1dc42ac2b779a48de8ef6be867b6131eed2b019f6b", size = 5552, upload-time = "2025-08-19T23:38:03.866Z" }, ] [[package]] @@ -1935,9 +1960,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 } +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 }, + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] [[package]] @@ -1947,9 +1972,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3c/f8/5dc70102e4d337063452c82e1f0d95e39abfe67aa222ed8a5ddeb9df8de8/requests_file-3.0.1.tar.gz", hash = "sha256:f14243d7796c588f3521bd423c5dea2ee4cc730e54a3cac9574d78aca1272576", size = 6967 } +sdist = { url = "https://files.pythonhosted.org/packages/3c/f8/5dc70102e4d337063452c82e1f0d95e39abfe67aa222ed8a5ddeb9df8de8/requests_file-3.0.1.tar.gz", hash = "sha256:f14243d7796c588f3521bd423c5dea2ee4cc730e54a3cac9574d78aca1272576", size = 6967, upload-time = "2025-10-20T18:56:42.279Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/d5/de8f089119205a09da657ed4784c584ede8381a0ce6821212a6d4ca47054/requests_file-3.0.1-py2.py3-none-any.whl", hash = "sha256:d0f5eb94353986d998f80ac63c7f146a307728be051d4d1cd390dbdb59c10fa2", size = 4514 }, + { url = "https://files.pythonhosted.org/packages/e1/d5/de8f089119205a09da657ed4784c584ede8381a0ce6821212a6d4ca47054/requests_file-3.0.1-py2.py3-none-any.whl", hash = "sha256:d0f5eb94353986d998f80ac63c7f146a307728be051d4d1cd390dbdb59c10fa2", size = 4514, upload-time = "2025-10-20T18:56:41.184Z" }, ] [[package]] @@ -1960,9 +1985,9 @@ dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393 }, + { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, ] [[package]] @@ -1974,62 +1999,62 @@ dependencies = [ { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/67/33/1a18839aaa8feef7983590c05c22c9c09d245ada6017d118325bbfcc7651/rich_toolkit-0.15.1.tar.gz", hash = "sha256:6f9630eb29f3843d19d48c3bd5706a086d36d62016687f9d0efa027ddc2dd08a", size = 115322 } +sdist = { url = "https://files.pythonhosted.org/packages/67/33/1a18839aaa8feef7983590c05c22c9c09d245ada6017d118325bbfcc7651/rich_toolkit-0.15.1.tar.gz", hash = "sha256:6f9630eb29f3843d19d48c3bd5706a086d36d62016687f9d0efa027ddc2dd08a", size = 115322, upload-time = "2025-09-04T09:28:11.789Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/49/42821d55ead7b5a87c8d121edf323cb393d8579f63e933002ade900b784f/rich_toolkit-0.15.1-py3-none-any.whl", hash = "sha256:36a0b1d9a135d26776e4b78f1d5c2655da6e0ef432380b5c6b523c8d8ab97478", size = 29412 }, + { url = "https://files.pythonhosted.org/packages/c8/49/42821d55ead7b5a87c8d121edf323cb393d8579f63e933002ade900b784f/rich_toolkit-0.15.1-py3-none-any.whl", hash = "sha256:36a0b1d9a135d26776e4b78f1d5c2655da6e0ef432380b5c6b523c8d8ab97478", size = 29412, upload-time = "2025-09-04T09:28:10.587Z" }, ] [[package]] name = "rignore" version = "0.7.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/8a/a4078f6e14932ac7edb171149c481de29969d96ddee3ece5dc4c26f9e0c3/rignore-0.7.6-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2bdab1d31ec9b4fb1331980ee49ea051c0d7f7bb6baa28b3125ef03cdc48fdaf", size = 883057 }, - { url = "https://files.pythonhosted.org/packages/f9/8f/f8daacd177db4bf7c2223bab41e630c52711f8af9ed279be2058d2fe4982/rignore-0.7.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:90f0a00ce0c866c275bf888271f1dc0d2140f29b82fcf33cdbda1e1a6af01010", size = 820150 }, - { url = "https://files.pythonhosted.org/packages/36/31/b65b837e39c3f7064c426754714ac633b66b8c2290978af9d7f513e14aa9/rignore-0.7.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1ad295537041dc2ed4b540fb1a3906bd9ede6ccdad3fe79770cd89e04e3c73c", size = 897406 }, - { url = "https://files.pythonhosted.org/packages/ca/58/1970ce006c427e202ac7c081435719a076c478f07b3a23f469227788dc23/rignore-0.7.6-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f782dbd3a65a5ac85adfff69e5c6b101285ef3f845c3a3cae56a54bebf9fe116", size = 874050 }, - { url = "https://files.pythonhosted.org/packages/d4/00/eb45db9f90137329072a732273be0d383cb7d7f50ddc8e0bceea34c1dfdf/rignore-0.7.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65cece3b36e5b0826d946494734c0e6aaf5a0337e18ff55b071438efe13d559e", size = 1167835 }, - { url = "https://files.pythonhosted.org/packages/f3/f1/6f1d72ddca41a64eed569680587a1236633587cc9f78136477ae69e2c88a/rignore-0.7.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d7e4bb66c13cd7602dc8931822c02dfbbd5252015c750ac5d6152b186f0a8be0", size = 941945 }, - { url = "https://files.pythonhosted.org/packages/48/6f/2f178af1c1a276a065f563ec1e11e7a9e23d4996fd0465516afce4b5c636/rignore-0.7.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297e500c15766e196f68aaaa70e8b6db85fa23fdc075b880d8231fdfba738cd7", size = 959067 }, - { url = "https://files.pythonhosted.org/packages/5b/db/423a81c4c1e173877c7f9b5767dcaf1ab50484a94f60a0b2ed78be3fa765/rignore-0.7.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a07084211a8d35e1a5b1d32b9661a5ed20669970b369df0cf77da3adea3405de", size = 984438 }, - { url = "https://files.pythonhosted.org/packages/31/eb/c4f92cc3f2825d501d3c46a244a671eb737fc1bcf7b05a3ecd34abb3e0d7/rignore-0.7.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:181eb2a975a22256a1441a9d2f15eb1292839ea3f05606620bd9e1938302cf79", size = 1078365 }, - { url = "https://files.pythonhosted.org/packages/26/09/99442f02794bd7441bfc8ed1c7319e890449b816a7493b2db0e30af39095/rignore-0.7.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:7bbcdc52b5bf9f054b34ce4af5269df5d863d9c2456243338bc193c28022bd7b", size = 1139066 }, - { url = "https://files.pythonhosted.org/packages/2c/88/bcfc21e520bba975410e9419450f4b90a2ac8236b9a80fd8130e87d098af/rignore-0.7.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f2e027a6da21a7c8c0d87553c24ca5cc4364def18d146057862c23a96546238e", size = 1118036 }, - { url = "https://files.pythonhosted.org/packages/e2/25/d37215e4562cda5c13312636393aea0bafe38d54d4e0517520a4cc0753ec/rignore-0.7.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee4a18b82cbbc648e4aac1510066682fe62beb5dc88e2c67c53a83954e541360", size = 1127550 }, - { url = "https://files.pythonhosted.org/packages/dc/76/a264ab38bfa1620ec12a8ff1c07778da89e16d8c0f3450b0333020d3d6dc/rignore-0.7.6-cp313-cp313-win32.whl", hash = "sha256:a7d7148b6e5e95035d4390396895adc384d37ff4e06781a36fe573bba7c283e5", size = 646097 }, - { url = "https://files.pythonhosted.org/packages/62/44/3c31b8983c29ea8832b6082ddb1d07b90379c2d993bd20fce4487b71b4f4/rignore-0.7.6-cp313-cp313-win_amd64.whl", hash = "sha256:b037c4b15a64dced08fc12310ee844ec2284c4c5c1ca77bc37d0a04f7bff386e", size = 726170 }, - { url = "https://files.pythonhosted.org/packages/aa/41/e26a075cab83debe41a42661262f606166157df84e0e02e2d904d134c0d8/rignore-0.7.6-cp313-cp313-win_arm64.whl", hash = "sha256:e47443de9b12fe569889bdbe020abe0e0b667516ee2ab435443f6d0869bd2804", size = 656184 }, - { url = "https://files.pythonhosted.org/packages/9a/b9/1f5bd82b87e5550cd843ceb3768b4a8ef274eb63f29333cf2f29644b3d75/rignore-0.7.6-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:8e41be9fa8f2f47239ded8920cc283699a052ac4c371f77f5ac017ebeed75732", size = 882632 }, - { url = "https://files.pythonhosted.org/packages/e9/6b/07714a3efe4a8048864e8a5b7db311ba51b921e15268b17defaebf56d3db/rignore-0.7.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6dc1e171e52cefa6c20e60c05394a71165663b48bca6c7666dee4f778f2a7d90", size = 820760 }, - { url = "https://files.pythonhosted.org/packages/ac/0f/348c829ea2d8d596e856371b14b9092f8a5dfbb62674ec9b3f67e4939a9d/rignore-0.7.6-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ce2268837c3600f82ab8db58f5834009dc638ee17103582960da668963bebc5", size = 899044 }, - { url = "https://files.pythonhosted.org/packages/f0/30/2e1841a19b4dd23878d73edd5d82e998a83d5ed9570a89675f140ca8b2ad/rignore-0.7.6-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:690a3e1b54bfe77e89c4bacb13f046e642f8baadafc61d68f5a726f324a76ab6", size = 874144 }, - { url = "https://files.pythonhosted.org/packages/c2/bf/0ce9beb2e5f64c30e3580bef09f5829236889f01511a125f98b83169b993/rignore-0.7.6-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09d12ac7a0b6210c07bcd145007117ebd8abe99c8eeb383e9e4673910c2754b2", size = 1168062 }, - { url = "https://files.pythonhosted.org/packages/b9/8b/571c178414eb4014969865317da8a02ce4cf5241a41676ef91a59aab24de/rignore-0.7.6-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a2b2b74a8c60203b08452479b90e5ce3dbe96a916214bc9eb2e5af0b6a9beb0", size = 942542 }, - { url = "https://files.pythonhosted.org/packages/19/62/7a3cf601d5a45137a7e2b89d10c05b5b86499190c4b7ca5c3c47d79ee519/rignore-0.7.6-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fc5a531ef02131e44359419a366bfac57f773ea58f5278c2cdd915f7d10ea94", size = 958739 }, - { url = "https://files.pythonhosted.org/packages/5f/1f/4261f6a0d7caf2058a5cde2f5045f565ab91aa7badc972b57d19ce58b14e/rignore-0.7.6-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7a1f77d9c4cd7e76229e252614d963442686bfe12c787a49f4fe481df49e7a9", size = 984138 }, - { url = "https://files.pythonhosted.org/packages/2b/bf/628dfe19c75e8ce1f45f7c248f5148b17dfa89a817f8e3552ab74c3ae812/rignore-0.7.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ead81f728682ba72b5b1c3d5846b011d3e0174da978de87c61645f2ed36659a7", size = 1079299 }, - { url = "https://files.pythonhosted.org/packages/af/a5/be29c50f5c0c25c637ed32db8758fdf5b901a99e08b608971cda8afb293b/rignore-0.7.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:12ffd50f520c22ffdabed8cd8bfb567d9ac165b2b854d3e679f4bcaef11a9441", size = 1139618 }, - { url = "https://files.pythonhosted.org/packages/2a/40/3c46cd7ce4fa05c20b525fd60f599165e820af66e66f2c371cd50644558f/rignore-0.7.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e5a16890fbe3c894f8ca34b0fcacc2c200398d4d46ae654e03bc9b3dbf2a0a72", size = 1117626 }, - { url = "https://files.pythonhosted.org/packages/8c/b9/aea926f263b8a29a23c75c2e0d8447965eb1879d3feb53cfcf84db67ed58/rignore-0.7.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3abab3bf99e8a77488ef6c7c9a799fac22224c28fe9f25cc21aa7cc2b72bfc0b", size = 1128144 }, - { url = "https://files.pythonhosted.org/packages/a4/f6/0d6242f8d0df7f2ecbe91679fefc1f75e7cd2072cb4f497abaab3f0f8523/rignore-0.7.6-cp314-cp314-win32.whl", hash = "sha256:eeef421c1782953c4375aa32f06ecae470c1285c6381eee2a30d2e02a5633001", size = 646385 }, - { url = "https://files.pythonhosted.org/packages/d5/38/c0dcd7b10064f084343d6af26fe9414e46e9619c5f3224b5272e8e5d9956/rignore-0.7.6-cp314-cp314-win_amd64.whl", hash = "sha256:6aeed503b3b3d5af939b21d72a82521701a4bd3b89cd761da1e7dc78621af304", size = 725738 }, - { url = "https://files.pythonhosted.org/packages/d9/7a/290f868296c1ece914d565757ab363b04730a728b544beb567ceb3b2d96f/rignore-0.7.6-cp314-cp314-win_arm64.whl", hash = "sha256:104f215b60b3c984c386c3e747d6ab4376d5656478694e22c7bd2f788ddd8304", size = 656008 }, - { url = "https://files.pythonhosted.org/packages/ca/d2/3c74e3cd81fe8ea08a8dcd2d755c09ac2e8ad8fe409508904557b58383d3/rignore-0.7.6-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bb24a5b947656dd94cb9e41c4bc8b23cec0c435b58be0d74a874f63c259549e8", size = 882835 }, - { url = "https://files.pythonhosted.org/packages/77/61/a772a34b6b63154877433ac2d048364815b24c2dd308f76b212c408101a2/rignore-0.7.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b1e33c9501cefe24b70a1eafd9821acfd0ebf0b35c3a379430a14df089993e3", size = 820301 }, - { url = "https://files.pythonhosted.org/packages/71/30/054880b09c0b1b61d17eeb15279d8bf729c0ba52b36c3ada52fb827cbb3c/rignore-0.7.6-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bec3994665a44454df86deb762061e05cd4b61e3772f5b07d1882a8a0d2748d5", size = 897611 }, - { url = "https://files.pythonhosted.org/packages/1e/40/b2d1c169f833d69931bf232600eaa3c7998ba4f9a402e43a822dad2ea9f2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26cba2edfe3cff1dfa72bddf65d316ddebf182f011f2f61538705d6dbaf54986", size = 873875 }, - { url = "https://files.pythonhosted.org/packages/55/59/ca5ae93d83a1a60e44b21d87deb48b177a8db1b85e82fc8a9abb24a8986d/rignore-0.7.6-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffa86694fec604c613696cb91e43892aa22e1fec5f9870e48f111c603e5ec4e9", size = 1167245 }, - { url = "https://files.pythonhosted.org/packages/a5/52/cf3dce392ba2af806cba265aad6bcd9c48bb2a6cb5eee448d3319f6e505b/rignore-0.7.6-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48efe2ed95aa8104145004afb15cdfa02bea5cdde8b0344afeb0434f0d989aa2", size = 941750 }, - { url = "https://files.pythonhosted.org/packages/ec/be/3f344c6218d779395e785091d05396dfd8b625f6aafbe502746fcd880af2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dcae43eb44b7f2457fef7cc87f103f9a0013017a6f4e62182c565e924948f21", size = 958896 }, - { url = "https://files.pythonhosted.org/packages/c9/34/d3fa71938aed7d00dcad87f0f9bcb02ad66c85d6ffc83ba31078ce53646a/rignore-0.7.6-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2cd649a7091c0dad2f11ef65630d30c698d505cbe8660dd395268e7c099cc99f", size = 983992 }, - { url = "https://files.pythonhosted.org/packages/24/a4/52a697158e9920705bdbd0748d59fa63e0f3233fb92e9df9a71afbead6ca/rignore-0.7.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42de84b0289d478d30ceb7ae59023f7b0527786a9a5b490830e080f0e4ea5aeb", size = 1078181 }, - { url = "https://files.pythonhosted.org/packages/ac/65/aa76dbcdabf3787a6f0fd61b5cc8ed1e88580590556d6c0207960d2384bb/rignore-0.7.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:875a617e57b53b4acbc5a91de418233849711c02e29cc1f4f9febb2f928af013", size = 1139232 }, - { url = "https://files.pythonhosted.org/packages/08/44/31b31a49b3233c6842acc1c0731aa1e7fb322a7170612acf30327f700b44/rignore-0.7.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8703998902771e96e49968105207719f22926e4431b108450f3f430b4e268b7c", size = 1117349 }, - { url = "https://files.pythonhosted.org/packages/e9/ae/1b199a2302c19c658cf74e5ee1427605234e8c91787cfba0015f2ace145b/rignore-0.7.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:602ef33f3e1b04c1e9a10a3c03f8bc3cef2d2383dcc250d309be42b49923cabc", size = 1127702 }, - { url = "https://files.pythonhosted.org/packages/fc/d3/18210222b37e87e36357f7b300b7d98c6dd62b133771e71ae27acba83a4f/rignore-0.7.6-cp314-cp314t-win32.whl", hash = "sha256:c1d8f117f7da0a4a96a8daef3da75bc090e3792d30b8b12cfadc240c631353f9", size = 647033 }, - { url = "https://files.pythonhosted.org/packages/3e/87/033eebfbee3ec7d92b3bb1717d8f68c88e6fc7de54537040f3b3a405726f/rignore-0.7.6-cp314-cp314t-win_amd64.whl", hash = "sha256:ca36e59408bec81de75d307c568c2d0d410fb880b1769be43611472c61e85c96", size = 725647 }, - { url = "https://files.pythonhosted.org/packages/79/62/b88e5879512c55b8ee979c666ee6902adc4ed05007226de266410ae27965/rignore-0.7.6-cp314-cp314t-win_arm64.whl", hash = "sha256:b83adabeb3e8cf662cabe1931b83e165b88c526fa6af6b3aa90429686e474896", size = 656035 }, +sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140, upload-time = "2025-11-05T21:41:21.968Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/8a/a4078f6e14932ac7edb171149c481de29969d96ddee3ece5dc4c26f9e0c3/rignore-0.7.6-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2bdab1d31ec9b4fb1331980ee49ea051c0d7f7bb6baa28b3125ef03cdc48fdaf", size = 883057, upload-time = "2025-11-05T20:42:42.741Z" }, + { url = "https://files.pythonhosted.org/packages/f9/8f/f8daacd177db4bf7c2223bab41e630c52711f8af9ed279be2058d2fe4982/rignore-0.7.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:90f0a00ce0c866c275bf888271f1dc0d2140f29b82fcf33cdbda1e1a6af01010", size = 820150, upload-time = "2025-11-05T20:42:26.545Z" }, + { url = "https://files.pythonhosted.org/packages/36/31/b65b837e39c3f7064c426754714ac633b66b8c2290978af9d7f513e14aa9/rignore-0.7.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1ad295537041dc2ed4b540fb1a3906bd9ede6ccdad3fe79770cd89e04e3c73c", size = 897406, upload-time = "2025-11-05T20:40:53.854Z" }, + { url = "https://files.pythonhosted.org/packages/ca/58/1970ce006c427e202ac7c081435719a076c478f07b3a23f469227788dc23/rignore-0.7.6-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f782dbd3a65a5ac85adfff69e5c6b101285ef3f845c3a3cae56a54bebf9fe116", size = 874050, upload-time = "2025-11-05T20:41:08.922Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/eb45db9f90137329072a732273be0d383cb7d7f50ddc8e0bceea34c1dfdf/rignore-0.7.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65cece3b36e5b0826d946494734c0e6aaf5a0337e18ff55b071438efe13d559e", size = 1167835, upload-time = "2025-11-05T20:41:24.997Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f1/6f1d72ddca41a64eed569680587a1236633587cc9f78136477ae69e2c88a/rignore-0.7.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d7e4bb66c13cd7602dc8931822c02dfbbd5252015c750ac5d6152b186f0a8be0", size = 941945, upload-time = "2025-11-05T20:41:40.628Z" }, + { url = "https://files.pythonhosted.org/packages/48/6f/2f178af1c1a276a065f563ec1e11e7a9e23d4996fd0465516afce4b5c636/rignore-0.7.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297e500c15766e196f68aaaa70e8b6db85fa23fdc075b880d8231fdfba738cd7", size = 959067, upload-time = "2025-11-05T20:42:11.09Z" }, + { url = "https://files.pythonhosted.org/packages/5b/db/423a81c4c1e173877c7f9b5767dcaf1ab50484a94f60a0b2ed78be3fa765/rignore-0.7.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a07084211a8d35e1a5b1d32b9661a5ed20669970b369df0cf77da3adea3405de", size = 984438, upload-time = "2025-11-05T20:41:55.443Z" }, + { url = "https://files.pythonhosted.org/packages/31/eb/c4f92cc3f2825d501d3c46a244a671eb737fc1bcf7b05a3ecd34abb3e0d7/rignore-0.7.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:181eb2a975a22256a1441a9d2f15eb1292839ea3f05606620bd9e1938302cf79", size = 1078365, upload-time = "2025-11-05T21:40:15.148Z" }, + { url = "https://files.pythonhosted.org/packages/26/09/99442f02794bd7441bfc8ed1c7319e890449b816a7493b2db0e30af39095/rignore-0.7.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:7bbcdc52b5bf9f054b34ce4af5269df5d863d9c2456243338bc193c28022bd7b", size = 1139066, upload-time = "2025-11-05T21:40:32.771Z" }, + { url = "https://files.pythonhosted.org/packages/2c/88/bcfc21e520bba975410e9419450f4b90a2ac8236b9a80fd8130e87d098af/rignore-0.7.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f2e027a6da21a7c8c0d87553c24ca5cc4364def18d146057862c23a96546238e", size = 1118036, upload-time = "2025-11-05T21:40:49.646Z" }, + { url = "https://files.pythonhosted.org/packages/e2/25/d37215e4562cda5c13312636393aea0bafe38d54d4e0517520a4cc0753ec/rignore-0.7.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee4a18b82cbbc648e4aac1510066682fe62beb5dc88e2c67c53a83954e541360", size = 1127550, upload-time = "2025-11-05T21:41:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/dc/76/a264ab38bfa1620ec12a8ff1c07778da89e16d8c0f3450b0333020d3d6dc/rignore-0.7.6-cp313-cp313-win32.whl", hash = "sha256:a7d7148b6e5e95035d4390396895adc384d37ff4e06781a36fe573bba7c283e5", size = 646097, upload-time = "2025-11-05T21:41:53.201Z" }, + { url = "https://files.pythonhosted.org/packages/62/44/3c31b8983c29ea8832b6082ddb1d07b90379c2d993bd20fce4487b71b4f4/rignore-0.7.6-cp313-cp313-win_amd64.whl", hash = "sha256:b037c4b15a64dced08fc12310ee844ec2284c4c5c1ca77bc37d0a04f7bff386e", size = 726170, upload-time = "2025-11-05T21:41:38.131Z" }, + { url = "https://files.pythonhosted.org/packages/aa/41/e26a075cab83debe41a42661262f606166157df84e0e02e2d904d134c0d8/rignore-0.7.6-cp313-cp313-win_arm64.whl", hash = "sha256:e47443de9b12fe569889bdbe020abe0e0b667516ee2ab435443f6d0869bd2804", size = 656184, upload-time = "2025-11-05T21:41:27.396Z" }, + { url = "https://files.pythonhosted.org/packages/9a/b9/1f5bd82b87e5550cd843ceb3768b4a8ef274eb63f29333cf2f29644b3d75/rignore-0.7.6-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:8e41be9fa8f2f47239ded8920cc283699a052ac4c371f77f5ac017ebeed75732", size = 882632, upload-time = "2025-11-05T20:42:44.063Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6b/07714a3efe4a8048864e8a5b7db311ba51b921e15268b17defaebf56d3db/rignore-0.7.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6dc1e171e52cefa6c20e60c05394a71165663b48bca6c7666dee4f778f2a7d90", size = 820760, upload-time = "2025-11-05T20:42:27.885Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0f/348c829ea2d8d596e856371b14b9092f8a5dfbb62674ec9b3f67e4939a9d/rignore-0.7.6-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ce2268837c3600f82ab8db58f5834009dc638ee17103582960da668963bebc5", size = 899044, upload-time = "2025-11-05T20:40:55.336Z" }, + { url = "https://files.pythonhosted.org/packages/f0/30/2e1841a19b4dd23878d73edd5d82e998a83d5ed9570a89675f140ca8b2ad/rignore-0.7.6-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:690a3e1b54bfe77e89c4bacb13f046e642f8baadafc61d68f5a726f324a76ab6", size = 874144, upload-time = "2025-11-05T20:41:10.195Z" }, + { url = "https://files.pythonhosted.org/packages/c2/bf/0ce9beb2e5f64c30e3580bef09f5829236889f01511a125f98b83169b993/rignore-0.7.6-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09d12ac7a0b6210c07bcd145007117ebd8abe99c8eeb383e9e4673910c2754b2", size = 1168062, upload-time = "2025-11-05T20:41:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/b9/8b/571c178414eb4014969865317da8a02ce4cf5241a41676ef91a59aab24de/rignore-0.7.6-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a2b2b74a8c60203b08452479b90e5ce3dbe96a916214bc9eb2e5af0b6a9beb0", size = 942542, upload-time = "2025-11-05T20:41:41.838Z" }, + { url = "https://files.pythonhosted.org/packages/19/62/7a3cf601d5a45137a7e2b89d10c05b5b86499190c4b7ca5c3c47d79ee519/rignore-0.7.6-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fc5a531ef02131e44359419a366bfac57f773ea58f5278c2cdd915f7d10ea94", size = 958739, upload-time = "2025-11-05T20:42:12.463Z" }, + { url = "https://files.pythonhosted.org/packages/5f/1f/4261f6a0d7caf2058a5cde2f5045f565ab91aa7badc972b57d19ce58b14e/rignore-0.7.6-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7a1f77d9c4cd7e76229e252614d963442686bfe12c787a49f4fe481df49e7a9", size = 984138, upload-time = "2025-11-05T20:41:56.775Z" }, + { url = "https://files.pythonhosted.org/packages/2b/bf/628dfe19c75e8ce1f45f7c248f5148b17dfa89a817f8e3552ab74c3ae812/rignore-0.7.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ead81f728682ba72b5b1c3d5846b011d3e0174da978de87c61645f2ed36659a7", size = 1079299, upload-time = "2025-11-05T21:40:16.639Z" }, + { url = "https://files.pythonhosted.org/packages/af/a5/be29c50f5c0c25c637ed32db8758fdf5b901a99e08b608971cda8afb293b/rignore-0.7.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:12ffd50f520c22ffdabed8cd8bfb567d9ac165b2b854d3e679f4bcaef11a9441", size = 1139618, upload-time = "2025-11-05T21:40:34.507Z" }, + { url = "https://files.pythonhosted.org/packages/2a/40/3c46cd7ce4fa05c20b525fd60f599165e820af66e66f2c371cd50644558f/rignore-0.7.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e5a16890fbe3c894f8ca34b0fcacc2c200398d4d46ae654e03bc9b3dbf2a0a72", size = 1117626, upload-time = "2025-11-05T21:40:51.494Z" }, + { url = "https://files.pythonhosted.org/packages/8c/b9/aea926f263b8a29a23c75c2e0d8447965eb1879d3feb53cfcf84db67ed58/rignore-0.7.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3abab3bf99e8a77488ef6c7c9a799fac22224c28fe9f25cc21aa7cc2b72bfc0b", size = 1128144, upload-time = "2025-11-05T21:41:09.169Z" }, + { url = "https://files.pythonhosted.org/packages/a4/f6/0d6242f8d0df7f2ecbe91679fefc1f75e7cd2072cb4f497abaab3f0f8523/rignore-0.7.6-cp314-cp314-win32.whl", hash = "sha256:eeef421c1782953c4375aa32f06ecae470c1285c6381eee2a30d2e02a5633001", size = 646385, upload-time = "2025-11-05T21:41:55.105Z" }, + { url = "https://files.pythonhosted.org/packages/d5/38/c0dcd7b10064f084343d6af26fe9414e46e9619c5f3224b5272e8e5d9956/rignore-0.7.6-cp314-cp314-win_amd64.whl", hash = "sha256:6aeed503b3b3d5af939b21d72a82521701a4bd3b89cd761da1e7dc78621af304", size = 725738, upload-time = "2025-11-05T21:41:39.736Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7a/290f868296c1ece914d565757ab363b04730a728b544beb567ceb3b2d96f/rignore-0.7.6-cp314-cp314-win_arm64.whl", hash = "sha256:104f215b60b3c984c386c3e747d6ab4376d5656478694e22c7bd2f788ddd8304", size = 656008, upload-time = "2025-11-05T21:41:29.028Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d2/3c74e3cd81fe8ea08a8dcd2d755c09ac2e8ad8fe409508904557b58383d3/rignore-0.7.6-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bb24a5b947656dd94cb9e41c4bc8b23cec0c435b58be0d74a874f63c259549e8", size = 882835, upload-time = "2025-11-05T20:42:45.443Z" }, + { url = "https://files.pythonhosted.org/packages/77/61/a772a34b6b63154877433ac2d048364815b24c2dd308f76b212c408101a2/rignore-0.7.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b1e33c9501cefe24b70a1eafd9821acfd0ebf0b35c3a379430a14df089993e3", size = 820301, upload-time = "2025-11-05T20:42:29.226Z" }, + { url = "https://files.pythonhosted.org/packages/71/30/054880b09c0b1b61d17eeb15279d8bf729c0ba52b36c3ada52fb827cbb3c/rignore-0.7.6-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bec3994665a44454df86deb762061e05cd4b61e3772f5b07d1882a8a0d2748d5", size = 897611, upload-time = "2025-11-05T20:40:56.475Z" }, + { url = "https://files.pythonhosted.org/packages/1e/40/b2d1c169f833d69931bf232600eaa3c7998ba4f9a402e43a822dad2ea9f2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26cba2edfe3cff1dfa72bddf65d316ddebf182f011f2f61538705d6dbaf54986", size = 873875, upload-time = "2025-11-05T20:41:11.561Z" }, + { url = "https://files.pythonhosted.org/packages/55/59/ca5ae93d83a1a60e44b21d87deb48b177a8db1b85e82fc8a9abb24a8986d/rignore-0.7.6-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffa86694fec604c613696cb91e43892aa22e1fec5f9870e48f111c603e5ec4e9", size = 1167245, upload-time = "2025-11-05T20:41:28.29Z" }, + { url = "https://files.pythonhosted.org/packages/a5/52/cf3dce392ba2af806cba265aad6bcd9c48bb2a6cb5eee448d3319f6e505b/rignore-0.7.6-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48efe2ed95aa8104145004afb15cdfa02bea5cdde8b0344afeb0434f0d989aa2", size = 941750, upload-time = "2025-11-05T20:41:43.111Z" }, + { url = "https://files.pythonhosted.org/packages/ec/be/3f344c6218d779395e785091d05396dfd8b625f6aafbe502746fcd880af2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dcae43eb44b7f2457fef7cc87f103f9a0013017a6f4e62182c565e924948f21", size = 958896, upload-time = "2025-11-05T20:42:13.784Z" }, + { url = "https://files.pythonhosted.org/packages/c9/34/d3fa71938aed7d00dcad87f0f9bcb02ad66c85d6ffc83ba31078ce53646a/rignore-0.7.6-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2cd649a7091c0dad2f11ef65630d30c698d505cbe8660dd395268e7c099cc99f", size = 983992, upload-time = "2025-11-05T20:41:58.022Z" }, + { url = "https://files.pythonhosted.org/packages/24/a4/52a697158e9920705bdbd0748d59fa63e0f3233fb92e9df9a71afbead6ca/rignore-0.7.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42de84b0289d478d30ceb7ae59023f7b0527786a9a5b490830e080f0e4ea5aeb", size = 1078181, upload-time = "2025-11-05T21:40:18.151Z" }, + { url = "https://files.pythonhosted.org/packages/ac/65/aa76dbcdabf3787a6f0fd61b5cc8ed1e88580590556d6c0207960d2384bb/rignore-0.7.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:875a617e57b53b4acbc5a91de418233849711c02e29cc1f4f9febb2f928af013", size = 1139232, upload-time = "2025-11-05T21:40:35.966Z" }, + { url = "https://files.pythonhosted.org/packages/08/44/31b31a49b3233c6842acc1c0731aa1e7fb322a7170612acf30327f700b44/rignore-0.7.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8703998902771e96e49968105207719f22926e4431b108450f3f430b4e268b7c", size = 1117349, upload-time = "2025-11-05T21:40:53.013Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ae/1b199a2302c19c658cf74e5ee1427605234e8c91787cfba0015f2ace145b/rignore-0.7.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:602ef33f3e1b04c1e9a10a3c03f8bc3cef2d2383dcc250d309be42b49923cabc", size = 1127702, upload-time = "2025-11-05T21:41:10.881Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d3/18210222b37e87e36357f7b300b7d98c6dd62b133771e71ae27acba83a4f/rignore-0.7.6-cp314-cp314t-win32.whl", hash = "sha256:c1d8f117f7da0a4a96a8daef3da75bc090e3792d30b8b12cfadc240c631353f9", size = 647033, upload-time = "2025-11-05T21:42:00.095Z" }, + { url = "https://files.pythonhosted.org/packages/3e/87/033eebfbee3ec7d92b3bb1717d8f68c88e6fc7de54537040f3b3a405726f/rignore-0.7.6-cp314-cp314t-win_amd64.whl", hash = "sha256:ca36e59408bec81de75d307c568c2d0d410fb880b1769be43611472c61e85c96", size = 725647, upload-time = "2025-11-05T21:41:44.449Z" }, + { url = "https://files.pythonhosted.org/packages/79/62/b88e5879512c55b8ee979c666ee6902adc4ed05007226de266410ae27965/rignore-0.7.6-cp314-cp314t-win_arm64.whl", hash = "sha256:b83adabeb3e8cf662cabe1931b83e165b88c526fa6af6b3aa90429686e474896", size = 656035, upload-time = "2025-11-05T21:41:31.13Z" }, ] [[package]] @@ -2039,35 +2064,35 @@ 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 } +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 }, + { 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.14.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/55/cccfca45157a2031dcbb5a462a67f7cf27f8b37d4b3b1cd7438f0f5c1df6/ruff-0.14.4.tar.gz", hash = "sha256:f459a49fe1085a749f15414ca76f61595f1a2cc8778ed7c279b6ca2e1fd19df3", size = 5587844 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/b9/67240254166ae1eaa38dec32265e9153ac53645a6c6670ed36ad00722af8/ruff-0.14.4-py3-none-linux_armv6l.whl", hash = "sha256:e6604613ffbcf2297cd5dcba0e0ac9bd0c11dc026442dfbb614504e87c349518", size = 12606781 }, - { url = "https://files.pythonhosted.org/packages/46/c8/09b3ab245d8652eafe5256ab59718641429f68681ee713ff06c5c549f156/ruff-0.14.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d99c0b52b6f0598acede45ee78288e5e9b4409d1ce7f661f0fa36d4cbeadf9a4", size = 12946765 }, - { url = "https://files.pythonhosted.org/packages/14/bb/1564b000219144bf5eed2359edc94c3590dd49d510751dad26202c18a17d/ruff-0.14.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9358d490ec030f1b51d048a7fd6ead418ed0826daf6149e95e30aa67c168af33", size = 11928120 }, - { url = "https://files.pythonhosted.org/packages/a3/92/d5f1770e9988cc0742fefaa351e840d9aef04ec24ae1be36f333f96d5704/ruff-0.14.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b40d27924f1f02dfa827b9c0712a13c0e4b108421665322218fc38caf615c2", size = 12370877 }, - { url = "https://files.pythonhosted.org/packages/e2/29/e9282efa55f1973d109faf839a63235575519c8ad278cc87a182a366810e/ruff-0.14.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f5e649052a294fe00818650712083cddc6cc02744afaf37202c65df9ea52efa5", size = 12408538 }, - { url = "https://files.pythonhosted.org/packages/8e/01/930ed6ecfce130144b32d77d8d69f5c610e6d23e6857927150adf5d7379a/ruff-0.14.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa082a8f878deeba955531f975881828fd6afd90dfa757c2b0808aadb437136e", size = 13141942 }, - { url = "https://files.pythonhosted.org/packages/6a/46/a9c89b42b231a9f487233f17a89cbef9d5acd538d9488687a02ad288fa6b/ruff-0.14.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1043c6811c2419e39011890f14d0a30470f19d47d197c4858b2787dfa698f6c8", size = 14544306 }, - { url = "https://files.pythonhosted.org/packages/78/96/9c6cf86491f2a6d52758b830b89b78c2ae61e8ca66b86bf5a20af73d20e6/ruff-0.14.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a9f3a936ac27fb7c2a93e4f4b943a662775879ac579a433291a6f69428722649", size = 14210427 }, - { url = "https://files.pythonhosted.org/packages/71/f4/0666fe7769a54f63e66404e8ff698de1dcde733e12e2fd1c9c6efb689cb5/ruff-0.14.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95643ffd209ce78bc113266b88fba3d39e0461f0cbc8b55fb92505030fb4a850", size = 13658488 }, - { url = "https://files.pythonhosted.org/packages/ee/79/6ad4dda2cfd55e41ac9ed6d73ef9ab9475b1eef69f3a85957210c74ba12c/ruff-0.14.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:456daa2fa1021bc86ca857f43fe29d5d8b3f0e55e9f90c58c317c1dcc2afc7b5", size = 13354908 }, - { url = "https://files.pythonhosted.org/packages/b5/60/f0b6990f740bb15c1588601d19d21bcc1bd5de4330a07222041678a8e04f/ruff-0.14.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f911bba769e4a9f51af6e70037bb72b70b45a16db5ce73e1f72aefe6f6d62132", size = 13587803 }, - { url = "https://files.pythonhosted.org/packages/c9/da/eaaada586f80068728338e0ef7f29ab3e4a08a692f92eb901a4f06bbff24/ruff-0.14.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:76158a7369b3979fa878612c623a7e5430c18b2fd1c73b214945c2d06337db67", size = 12279654 }, - { url = "https://files.pythonhosted.org/packages/66/d4/b1d0e82cf9bf8aed10a6d45be47b3f402730aa2c438164424783ac88c0ed/ruff-0.14.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f3b8f3b442d2b14c246e7aeca2e75915159e06a3540e2f4bed9f50d062d24469", size = 12357520 }, - { url = "https://files.pythonhosted.org/packages/04/f4/53e2b42cc82804617e5c7950b7079d79996c27e99c4652131c6a1100657f/ruff-0.14.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c62da9a06779deecf4d17ed04939ae8b31b517643b26370c3be1d26f3ef7dbde", size = 12719431 }, - { url = "https://files.pythonhosted.org/packages/a2/94/80e3d74ed9a72d64e94a7b7706b1c1ebaa315ef2076fd33581f6a1cd2f95/ruff-0.14.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5a443a83a1506c684e98acb8cb55abaf3ef725078be40237463dae4463366349", size = 13464394 }, - { url = "https://files.pythonhosted.org/packages/54/1a/a49f071f04c42345c793d22f6cf5e0920095e286119ee53a64a3a3004825/ruff-0.14.4-py3-none-win32.whl", hash = "sha256:643b69cb63cd996f1fc7229da726d07ac307eae442dd8974dbc7cf22c1e18fff", size = 12493429 }, - { url = "https://files.pythonhosted.org/packages/bc/22/e58c43e641145a2b670328fb98bc384e20679b5774258b1e540207580266/ruff-0.14.4-py3-none-win_amd64.whl", hash = "sha256:26673da283b96fe35fa0c939bf8411abec47111644aa9f7cfbd3c573fb125d2c", size = 13635380 }, - { url = "https://files.pythonhosted.org/packages/30/bd/4168a751ddbbf43e86544b4de8b5c3b7be8d7167a2a5cb977d274e04f0a1/ruff-0.14.4-py3-none-win_arm64.whl", hash = "sha256:dd09c292479596b0e6fec8cd95c65c3a6dc68e9ad17b8f2382130f87ff6a75bb", size = 12663065 }, +sdist = { url = "https://files.pythonhosted.org/packages/df/55/cccfca45157a2031dcbb5a462a67f7cf27f8b37d4b3b1cd7438f0f5c1df6/ruff-0.14.4.tar.gz", hash = "sha256:f459a49fe1085a749f15414ca76f61595f1a2cc8778ed7c279b6ca2e1fd19df3", size = 5587844, upload-time = "2025-11-06T22:07:45.033Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/b9/67240254166ae1eaa38dec32265e9153ac53645a6c6670ed36ad00722af8/ruff-0.14.4-py3-none-linux_armv6l.whl", hash = "sha256:e6604613ffbcf2297cd5dcba0e0ac9bd0c11dc026442dfbb614504e87c349518", size = 12606781, upload-time = "2025-11-06T22:07:01.841Z" }, + { url = "https://files.pythonhosted.org/packages/46/c8/09b3ab245d8652eafe5256ab59718641429f68681ee713ff06c5c549f156/ruff-0.14.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d99c0b52b6f0598acede45ee78288e5e9b4409d1ce7f661f0fa36d4cbeadf9a4", size = 12946765, upload-time = "2025-11-06T22:07:05.858Z" }, + { url = "https://files.pythonhosted.org/packages/14/bb/1564b000219144bf5eed2359edc94c3590dd49d510751dad26202c18a17d/ruff-0.14.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9358d490ec030f1b51d048a7fd6ead418ed0826daf6149e95e30aa67c168af33", size = 11928120, upload-time = "2025-11-06T22:07:08.023Z" }, + { url = "https://files.pythonhosted.org/packages/a3/92/d5f1770e9988cc0742fefaa351e840d9aef04ec24ae1be36f333f96d5704/ruff-0.14.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b40d27924f1f02dfa827b9c0712a13c0e4b108421665322218fc38caf615c2", size = 12370877, upload-time = "2025-11-06T22:07:10.015Z" }, + { url = "https://files.pythonhosted.org/packages/e2/29/e9282efa55f1973d109faf839a63235575519c8ad278cc87a182a366810e/ruff-0.14.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f5e649052a294fe00818650712083cddc6cc02744afaf37202c65df9ea52efa5", size = 12408538, upload-time = "2025-11-06T22:07:13.085Z" }, + { url = "https://files.pythonhosted.org/packages/8e/01/930ed6ecfce130144b32d77d8d69f5c610e6d23e6857927150adf5d7379a/ruff-0.14.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa082a8f878deeba955531f975881828fd6afd90dfa757c2b0808aadb437136e", size = 13141942, upload-time = "2025-11-06T22:07:15.386Z" }, + { url = "https://files.pythonhosted.org/packages/6a/46/a9c89b42b231a9f487233f17a89cbef9d5acd538d9488687a02ad288fa6b/ruff-0.14.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1043c6811c2419e39011890f14d0a30470f19d47d197c4858b2787dfa698f6c8", size = 14544306, upload-time = "2025-11-06T22:07:17.631Z" }, + { url = "https://files.pythonhosted.org/packages/78/96/9c6cf86491f2a6d52758b830b89b78c2ae61e8ca66b86bf5a20af73d20e6/ruff-0.14.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a9f3a936ac27fb7c2a93e4f4b943a662775879ac579a433291a6f69428722649", size = 14210427, upload-time = "2025-11-06T22:07:19.832Z" }, + { url = "https://files.pythonhosted.org/packages/71/f4/0666fe7769a54f63e66404e8ff698de1dcde733e12e2fd1c9c6efb689cb5/ruff-0.14.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95643ffd209ce78bc113266b88fba3d39e0461f0cbc8b55fb92505030fb4a850", size = 13658488, upload-time = "2025-11-06T22:07:22.32Z" }, + { url = "https://files.pythonhosted.org/packages/ee/79/6ad4dda2cfd55e41ac9ed6d73ef9ab9475b1eef69f3a85957210c74ba12c/ruff-0.14.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:456daa2fa1021bc86ca857f43fe29d5d8b3f0e55e9f90c58c317c1dcc2afc7b5", size = 13354908, upload-time = "2025-11-06T22:07:24.347Z" }, + { url = "https://files.pythonhosted.org/packages/b5/60/f0b6990f740bb15c1588601d19d21bcc1bd5de4330a07222041678a8e04f/ruff-0.14.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f911bba769e4a9f51af6e70037bb72b70b45a16db5ce73e1f72aefe6f6d62132", size = 13587803, upload-time = "2025-11-06T22:07:26.327Z" }, + { url = "https://files.pythonhosted.org/packages/c9/da/eaaada586f80068728338e0ef7f29ab3e4a08a692f92eb901a4f06bbff24/ruff-0.14.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:76158a7369b3979fa878612c623a7e5430c18b2fd1c73b214945c2d06337db67", size = 12279654, upload-time = "2025-11-06T22:07:28.46Z" }, + { url = "https://files.pythonhosted.org/packages/66/d4/b1d0e82cf9bf8aed10a6d45be47b3f402730aa2c438164424783ac88c0ed/ruff-0.14.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f3b8f3b442d2b14c246e7aeca2e75915159e06a3540e2f4bed9f50d062d24469", size = 12357520, upload-time = "2025-11-06T22:07:31.468Z" }, + { url = "https://files.pythonhosted.org/packages/04/f4/53e2b42cc82804617e5c7950b7079d79996c27e99c4652131c6a1100657f/ruff-0.14.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c62da9a06779deecf4d17ed04939ae8b31b517643b26370c3be1d26f3ef7dbde", size = 12719431, upload-time = "2025-11-06T22:07:33.831Z" }, + { url = "https://files.pythonhosted.org/packages/a2/94/80e3d74ed9a72d64e94a7b7706b1c1ebaa315ef2076fd33581f6a1cd2f95/ruff-0.14.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5a443a83a1506c684e98acb8cb55abaf3ef725078be40237463dae4463366349", size = 13464394, upload-time = "2025-11-06T22:07:35.905Z" }, + { url = "https://files.pythonhosted.org/packages/54/1a/a49f071f04c42345c793d22f6cf5e0920095e286119ee53a64a3a3004825/ruff-0.14.4-py3-none-win32.whl", hash = "sha256:643b69cb63cd996f1fc7229da726d07ac307eae442dd8974dbc7cf22c1e18fff", size = 12493429, upload-time = "2025-11-06T22:07:38.43Z" }, + { url = "https://files.pythonhosted.org/packages/bc/22/e58c43e641145a2b670328fb98bc384e20679b5774258b1e540207580266/ruff-0.14.4-py3-none-win_amd64.whl", hash = "sha256:26673da283b96fe35fa0c939bf8411abec47111644aa9f7cfbd3c573fb125d2c", size = 13635380, upload-time = "2025-11-06T22:07:40.496Z" }, + { url = "https://files.pythonhosted.org/packages/30/bd/4168a751ddbbf43e86544b4de8b5c3b7be8d7167a2a5cb977d274e04f0a1/ruff-0.14.4-py3-none-win_arm64.whl", hash = "sha256:dd09c292479596b0e6fec8cd95c65c3a6dc68e9ad17b8f2382130f87ff6a75bb", size = 12663065, upload-time = "2025-11-06T22:07:42.603Z" }, ] [[package]] @@ -2077,9 +2102,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/62/74/8d69dcb7a9efe8baa2046891735e5dfe433ad558ae23d9e3c14c633d1d58/s3transfer-0.14.0.tar.gz", hash = "sha256:eff12264e7c8b4985074ccce27a3b38a485bb7f7422cc8046fee9be4983e4125", size = 151547 } +sdist = { url = "https://files.pythonhosted.org/packages/62/74/8d69dcb7a9efe8baa2046891735e5dfe433ad558ae23d9e3c14c633d1d58/s3transfer-0.14.0.tar.gz", hash = "sha256:eff12264e7c8b4985074ccce27a3b38a485bb7f7422cc8046fee9be4983e4125", size = 151547, upload-time = "2025-09-09T19:23:31.089Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/f0/ae7ca09223a81a1d890b2557186ea015f6e0502e9b8cb8e1813f1d8cfa4e/s3transfer-0.14.0-py3-none-any.whl", hash = "sha256:ea3b790c7077558ed1f02a3072fb3cb992bbbd253392f4b6e9e8976941c7d456", size = 85712 }, + { url = "https://files.pythonhosted.org/packages/48/f0/ae7ca09223a81a1d890b2557186ea015f6e0502e9b8cb8e1813f1d8cfa4e/s3transfer-0.14.0-py3-none-any.whl", hash = "sha256:ea3b790c7077558ed1f02a3072fb3cb992bbbd253392f4b6e9e8976941c7d456", size = 85712, upload-time = "2025-09-09T19:23:30.041Z" }, ] [[package]] @@ -2090,45 +2115,45 @@ dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/18/09875b4323b03ca9025bae7e6539797b27e4fc032998a466b4b9c3d24653/sentry_sdk-2.43.0.tar.gz", hash = "sha256:52ed6e251c5d2c084224d73efee56b007ef5c2d408a4a071270e82131d336e20", size = 368953 } +sdist = { url = "https://files.pythonhosted.org/packages/b3/18/09875b4323b03ca9025bae7e6539797b27e4fc032998a466b4b9c3d24653/sentry_sdk-2.43.0.tar.gz", hash = "sha256:52ed6e251c5d2c084224d73efee56b007ef5c2d408a4a071270e82131d336e20", size = 368953, upload-time = "2025-10-29T11:26:08.156Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/31/8228fa962f7fd8814d634e4ebece8780e2cdcfbdf0cd2e14d4a6861a7cd5/sentry_sdk-2.43.0-py2.py3-none-any.whl", hash = "sha256:4aacafcf1756ef066d359ae35030881917160ba7f6fc3ae11e0e58b09edc2d5d", size = 400997 }, + { url = "https://files.pythonhosted.org/packages/69/31/8228fa962f7fd8814d634e4ebece8780e2cdcfbdf0cd2e14d4a6861a7cd5/sentry_sdk-2.43.0-py2.py3-none-any.whl", hash = "sha256:4aacafcf1756ef066d359ae35030881917160ba7f6fc3ae11e0e58b09edc2d5d", size = 400997, upload-time = "2025-10-29T11:26:05.77Z" }, ] [[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 } +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 }, + { 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 } +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 }, + { 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 = "sniffio" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] [[package]] name = "soupsieve" version = "2.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472 } +sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679 }, + { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" }, ] [[package]] @@ -2142,9 +2167,9 @@ dependencies = [ { name = "starlette" }, { name = "wtforms" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/0c/614041e1b544e0de1f43b58f0105b3e2795b80369d5b0ff7412882d42fff/sqladmin-0.21.0.tar.gz", hash = "sha256:cb455b79eb79ef7d904680dd83817bf7750675147400b5b7cc401d04bda7ef2c", size = 1428312 } +sdist = { url = "https://files.pythonhosted.org/packages/5b/0c/614041e1b544e0de1f43b58f0105b3e2795b80369d5b0ff7412882d42fff/sqladmin-0.21.0.tar.gz", hash = "sha256:cb455b79eb79ef7d904680dd83817bf7750675147400b5b7cc401d04bda7ef2c", size = 1428312, upload-time = "2025-07-02T09:41:21.207Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/8d/81b2a48cc6f5479cb1148292518e3006ec8f5fbe3b0829ef165984e9d7b9/sqladmin-0.21.0-py3-none-any.whl", hash = "sha256:2b1802c49bdd3128c6452625705693cf32d5d33e7db30e63f409bd20a9c05b53", size = 1443585 }, + { url = "https://files.pythonhosted.org/packages/ed/8d/81b2a48cc6f5479cb1148292518e3006ec8f5fbe3b0829ef165984e9d7b9/sqladmin-0.21.0-py3-none-any.whl", hash = "sha256:2b1802c49bdd3128c6452625705693cf32d5d33e7db30e63f409bd20a9c05b53", size = 1443585, upload-time = "2025-07-02T09:41:19.205Z" }, ] [[package]] @@ -2155,17 +2180,17 @@ 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/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830 } +sdist = { url = "https://files.pythonhosted.org/packages/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830, upload-time = "2025-10-10T14:39:12.935Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/d3/c67077a2249fdb455246e6853166360054c331db4613cda3e31ab1cadbef/sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1", size = 2135479 }, - { url = "https://files.pythonhosted.org/packages/2b/91/eabd0688330d6fd114f5f12c4f89b0d02929f525e6bf7ff80aa17ca802af/sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45", size = 2123212 }, - { url = "https://files.pythonhosted.org/packages/b0/bb/43e246cfe0e81c018076a16036d9b548c4cc649de241fa27d8d9ca6f85ab/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976", size = 3255353 }, - { url = "https://files.pythonhosted.org/packages/b9/96/c6105ed9a880abe346b64d3b6ddef269ddfcab04f7f3d90a0bf3c5a88e82/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c", size = 3260222 }, - { url = "https://files.pythonhosted.org/packages/44/16/1857e35a47155b5ad927272fee81ae49d398959cb749edca6eaa399b582f/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d", size = 3189614 }, - { url = "https://files.pythonhosted.org/packages/88/ee/4afb39a8ee4fc786e2d716c20ab87b5b1fb33d4ac4129a1aaa574ae8a585/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40", size = 3226248 }, - { url = "https://files.pythonhosted.org/packages/32/d5/0e66097fc64fa266f29a7963296b40a80d6a997b7ac13806183700676f86/sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73", size = 2101275 }, - { url = "https://files.pythonhosted.org/packages/03/51/665617fe4f8c6450f42a6d8d69243f9420f5677395572c2fe9d21b493b7b/sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e", size = 2127901 }, - { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718 }, + { url = "https://files.pythonhosted.org/packages/45/d3/c67077a2249fdb455246e6853166360054c331db4613cda3e31ab1cadbef/sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1", size = 2135479, upload-time = "2025-10-10T16:03:37.671Z" }, + { url = "https://files.pythonhosted.org/packages/2b/91/eabd0688330d6fd114f5f12c4f89b0d02929f525e6bf7ff80aa17ca802af/sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45", size = 2123212, upload-time = "2025-10-10T16:03:41.755Z" }, + { url = "https://files.pythonhosted.org/packages/b0/bb/43e246cfe0e81c018076a16036d9b548c4cc649de241fa27d8d9ca6f85ab/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976", size = 3255353, upload-time = "2025-10-10T15:35:31.221Z" }, + { url = "https://files.pythonhosted.org/packages/b9/96/c6105ed9a880abe346b64d3b6ddef269ddfcab04f7f3d90a0bf3c5a88e82/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c", size = 3260222, upload-time = "2025-10-10T15:43:50.124Z" }, + { url = "https://files.pythonhosted.org/packages/44/16/1857e35a47155b5ad927272fee81ae49d398959cb749edca6eaa399b582f/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d", size = 3189614, upload-time = "2025-10-10T15:35:32.578Z" }, + { url = "https://files.pythonhosted.org/packages/88/ee/4afb39a8ee4fc786e2d716c20ab87b5b1fb33d4ac4129a1aaa574ae8a585/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40", size = 3226248, upload-time = "2025-10-10T15:43:51.862Z" }, + { url = "https://files.pythonhosted.org/packages/32/d5/0e66097fc64fa266f29a7963296b40a80d6a997b7ac13806183700676f86/sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73", size = 2101275, upload-time = "2025-10-10T15:03:26.096Z" }, + { url = "https://files.pythonhosted.org/packages/03/51/665617fe4f8c6450f42a6d8d69243f9420f5677395572c2fe9d21b493b7b/sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e", size = 2127901, upload-time = "2025-10-10T15:03:27.548Z" }, + { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718, upload-time = "2025-10-10T15:29:45.32Z" }, ] [package.optional-dependencies] @@ -2181,9 +2206,9 @@ dependencies = [ { name = "pydantic" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/5a/693d90866233e837d182da76082a6d4c2303f54d3aaaa5c78e1238c5d863/sqlmodel-0.0.27.tar.gz", hash = "sha256:ad1227f2014a03905aef32e21428640848ac09ff793047744a73dfdd077ff620", size = 118053 } +sdist = { url = "https://files.pythonhosted.org/packages/90/5a/693d90866233e837d182da76082a6d4c2303f54d3aaaa5c78e1238c5d863/sqlmodel-0.0.27.tar.gz", hash = "sha256:ad1227f2014a03905aef32e21428640848ac09ff793047744a73dfdd077ff620", size = 118053, upload-time = "2025-10-08T16:39:11.938Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/92/c35e036151fe53822893979f8a13e6f235ae8191f4164a79ae60a95d66aa/sqlmodel-0.0.27-py3-none-any.whl", hash = "sha256:667fe10aa8ff5438134668228dc7d7a08306f4c5c4c7e6ad3ad68defa0e7aa49", size = 29131 }, + { url = "https://files.pythonhosted.org/packages/8c/92/c35e036151fe53822893979f8a13e6f235ae8191f4164a79ae60a95d66aa/sqlmodel-0.0.27-py3-none-any.whl", hash = "sha256:667fe10aa8ff5438134668228dc7d7a08306f4c5c4c7e6ad3ad68defa0e7aa49", size = 29131, upload-time = "2025-10-08T16:39:10.917Z" }, ] [[package]] @@ -2193,18 +2218,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/1a/608df0b10b53b0beb96a37854ee05864d182ddd4b1156a22f1ad3860425a/starlette-0.49.3.tar.gz", hash = "sha256:1c14546f299b5901a1ea0e34410575bc33bbd741377a10484a54445588d00284", size = 2655031 } +sdist = { url = "https://files.pythonhosted.org/packages/de/1a/608df0b10b53b0beb96a37854ee05864d182ddd4b1156a22f1ad3860425a/starlette-0.49.3.tar.gz", hash = "sha256:1c14546f299b5901a1ea0e34410575bc33bbd741377a10484a54445588d00284", size = 2655031, upload-time = "2025-11-01T15:12:26.13Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/e0/021c772d6a662f43b63044ab481dc6ac7592447605b5b35a957785363122/starlette-0.49.3-py3-none-any.whl", hash = "sha256:b579b99715fdc2980cf88c8ec96d3bf1ce16f5a8051a7c2b84ef9b1cdecaea2f", size = 74340 }, + { url = "https://files.pythonhosted.org/packages/a3/e0/021c772d6a662f43b63044ab481dc6ac7592447605b5b35a957785363122/starlette-0.49.3-py3-none-any.whl", hash = "sha256:b579b99715fdc2980cf88c8ec96d3bf1ce16f5a8051a7c2b84ef9b1cdecaea2f", size = 74340, upload-time = "2025-11-01T15:12:24.387Z" }, ] [[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 } +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 }, + { 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]] @@ -2217,9 +2242,9 @@ dependencies = [ { name = "requests" }, { name = "requests-file" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/78/182641ea38e3cfd56e9c7b3c0d48a53d432eea755003aa544af96403d4ac/tldextract-5.3.0.tar.gz", hash = "sha256:b3d2b70a1594a0ecfa6967d57251527d58e00bb5a91a74387baa0d87a0678609", size = 128502 } +sdist = { url = "https://files.pythonhosted.org/packages/97/78/182641ea38e3cfd56e9c7b3c0d48a53d432eea755003aa544af96403d4ac/tldextract-5.3.0.tar.gz", hash = "sha256:b3d2b70a1594a0ecfa6967d57251527d58e00bb5a91a74387baa0d87a0678609", size = 128502, upload-time = "2025-04-22T06:19:37.491Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/67/7c/ea488ef48f2f544566947ced88541bc45fae9e0e422b2edbf165ee07da99/tldextract-5.3.0-py3-none-any.whl", hash = "sha256:f70f31d10b55c83993f55e91ecb7c5d84532a8972f22ec578ecfbe5ea2292db2", size = 107384 }, + { url = "https://files.pythonhosted.org/packages/67/7c/ea488ef48f2f544566947ced88541bc45fae9e0e422b2edbf165ee07da99/tldextract-5.3.0-py3-none-any.whl", hash = "sha256:f70f31d10b55c83993f55e91ecb7c5d84532a8972f22ec578ecfbe5ea2292db2", size = 107384, upload-time = "2025-04-22T06:19:36.304Z" }, ] [[package]] @@ -2232,18 +2257,18 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8f/28/7c85c8032b91dbe79725b6f17d2fffc595dff06a35c7a30a37bef73a1ab4/typer-0.20.0.tar.gz", hash = "sha256:1aaf6494031793e4876fb0bacfa6a912b551cf43c1e63c800df8b1a866720c37", size = 106492 } +sdist = { url = "https://files.pythonhosted.org/packages/8f/28/7c85c8032b91dbe79725b6f17d2fffc595dff06a35c7a30a37bef73a1ab4/typer-0.20.0.tar.gz", hash = "sha256:1aaf6494031793e4876fb0bacfa6a912b551cf43c1e63c800df8b1a866720c37", size = 106492, upload-time = "2025-10-20T17:03:49.445Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/64/7713ffe4b5983314e9d436a90d5bd4f63b6054e2aca783a3cfc44cb95bbf/typer-0.20.0-py3-none-any.whl", hash = "sha256:5b463df6793ec1dca6213a3cf4c0f03bc6e322ac5e16e13ddd622a889489784a", size = 47028 }, + { url = "https://files.pythonhosted.org/packages/78/64/7713ffe4b5983314e9d436a90d5bd4f63b6054e2aca783a3cfc44cb95bbf/typer-0.20.0-py3-none-any.whl", hash = "sha256:5b463df6793ec1dca6213a3cf4c0f03bc6e322ac5e16e13ddd622a889489784a", size = 47028, upload-time = "2025-10-20T17:03:47.617Z" }, ] [[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 } +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 }, + { 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]] @@ -2253,36 +2278,36 @@ 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 } +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 }, + { 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 = "2025.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380 } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839 }, + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, ] [[package]] name = "uritemplate" version = "4.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/60/f174043244c5306c9988380d2cb10009f91563fc4b31293d27e17201af56/uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e", size = 33267 } +sdist = { url = "https://files.pythonhosted.org/packages/98/60/f174043244c5306c9988380d2cb10009f91563fc4b31293d27e17201af56/uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e", size = 33267, upload-time = "2025-06-02T15:12:06.318Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/99/3ae339466c9183ea5b8ae87b34c0b897eda475d2aec2307cae60e5cd4f29/uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686", size = 11488 }, + { url = "https://files.pythonhosted.org/packages/a9/99/3ae339466c9183ea5b8ae87b34c0b897eda475d2aec2307cae60e5cd4f29/uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686", size = 11488, upload-time = "2025-06-02T15:12:03.405Z" }, ] [[package]] name = "urllib3" version = "2.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185 } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795 }, + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, ] [[package]] @@ -2293,9 +2318,9 @@ dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605 } +sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605, upload-time = "2025-10-18T13:46:44.63Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109 }, + { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109, upload-time = "2025-10-18T13:46:42.958Z" }, ] [package.optional-dependencies] @@ -2313,26 +2338,26 @@ standard = [ name = "uvloop" version = "0.22.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611 }, - { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811 }, - { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562 }, - { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890 }, - { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472 }, - { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051 }, - { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067 }, - { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423 }, - { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437 }, - { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101 }, - { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158 }, - { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360 }, - { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790 }, - { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783 }, - { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548 }, - { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065 }, - { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384 }, - { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730 }, +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, + { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, + { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, + { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, + { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, + { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, + { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, + { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, + { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, + { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, ] [[package]] @@ -2342,74 +2367,74 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321 }, - { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783 }, - { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279 }, - { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405 }, - { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976 }, - { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506 }, - { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936 }, - { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147 }, - { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007 }, - { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280 }, - { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056 }, - { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162 }, - { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909 }, - { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389 }, - { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964 }, - { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114 }, - { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264 }, - { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877 }, - { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176 }, - { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577 }, - { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425 }, - { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826 }, - { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208 }, - { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315 }, - { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869 }, - { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919 }, - { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845 }, - { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027 }, - { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615 }, - { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836 }, - { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099 }, - { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626 }, - { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519 }, - { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078 }, - { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664 }, - { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154 }, - { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820 }, - { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510 }, - { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408 }, - { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968 }, - { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096 }, - { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040 }, - { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847 }, - { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072 }, - { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104 }, - { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112 }, +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, + { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, + { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, + { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, + { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, + { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, + { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, + { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, + { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, + { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, + { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, + { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, + { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, + { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, + { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, + { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, + { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, + { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, + { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, + { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, + { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, + { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, + { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, + { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, + { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, + { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, + { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, + { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, + { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, + { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, + { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, + { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, + { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, ] [[package]] name = "websockets" version = "15.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440 }, - { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098 }, - { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329 }, - { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111 }, - { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054 }, - { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496 }, - { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829 }, - { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217 }, - { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195 }, - { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393 }, - { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837 }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 }, + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, ] [[package]] @@ -2419,7 +2444,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6a/c7/96d10183c3470f1836846f7b9527d6cb0b6c2226ebca40f36fa29f23de60/wtforms-3.1.2.tar.gz", hash = "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9", size = 134705 } +sdist = { url = "https://files.pythonhosted.org/packages/6a/c7/96d10183c3470f1836846f7b9527d6cb0b6c2226ebca40f36fa29f23de60/wtforms-3.1.2.tar.gz", hash = "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9", size = 134705, upload-time = "2024-01-06T07:52:41.075Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/19/c3232f35e24dccfad372e9f341c4f3a1166ae7c66e4e1351a9467c921cc1/wtforms-3.1.2-py3-none-any.whl", hash = "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07", size = 145961 }, + { url = "https://files.pythonhosted.org/packages/18/19/c3232f35e24dccfad372e9f341c4f3a1166ae7c66e4e1351a9467c921cc1/wtforms-3.1.2-py3-none-any.whl", hash = "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07", size = 145961, upload-time = "2024-01-06T07:52:43.023Z" }, ] diff --git a/codemeta.json b/codemeta.json index 9d940b3..f3704e5 100644 --- a/codemeta.json +++ b/codemeta.json @@ -42,7 +42,7 @@ ], "license": "https://spdx.org/licenses/AGPL-3.0-or-later", "name": "Reverse Engineering Lab", - "programmingLanguage": ["Python 3.13", "JavaScript"], + "programmingLanguage": ["Python 3.14", "JavaScript"], "softwareRequirements": "Docker", "version": "0.1.0" } From 5bd435c30f063ede66c2c2c03fd6bc50dae5bd30 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Sat, 8 Nov 2025 11:02:31 +0100 Subject: [PATCH 35/79] fix(backend): Init disposable email checker without Redis if no Redis connection available --- .../app/api/auth/utils/email_validation.py | 21 ++++++++++++------- backend/app/main.py | 13 ++++++------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/backend/app/api/auth/utils/email_validation.py b/backend/app/api/auth/utils/email_validation.py index 79e908b..6d5275f 100644 --- a/backend/app/api/auth/utils/email_validation.py +++ b/backend/app/api/auth/utils/email_validation.py @@ -18,7 +18,7 @@ class EmailChecker: """Email checker that manages disposable domain validation.""" - def __init__(self, redis_client: Redis) -> None: + def __init__(self, redis_client: Redis | None) -> None: """Initialize email checker with Redis client. Args: @@ -34,13 +34,17 @@ async def initialize(self) -> None: Should be called during application startup. """ try: - self.checker = DefaultChecker( - source=DISPOSABLE_DOMAINS_URL, - db_provider="redis", - redis_client=self.redis_client, + if self.redis_client is None: + self.checker = DefaultChecker(source=DISPOSABLE_DOMAINS_URL) + logger.info("Disposable email checker initialized without Redis") + else: + self.checker = DefaultChecker( + source=DISPOSABLE_DOMAINS_URL, + db_provider="redis", + redis_client=self.redis_client, ) - await self.checker.init_redis() - logger.info("Disposable email checker initialized successfully") + await self.checker.init_redis() + logger.info("Disposable email checker initialized with Redis") # Fetch initial domains await self._refresh_domains() @@ -87,7 +91,8 @@ async def close(self) -> None: await self._refresh_task # Close checker connections if initialized - if self.checker is not None: + if self.checker is not None and self.redis_client is not None: + logger.info("Closing email checker Redis connections") try: await self.checker.close_connections() logger.info("Email checker closed successfully") diff --git a/backend/app/main.py b/backend/app/main.py index 7d0757f..4b8807b 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -40,13 +40,12 @@ async def lifespan(app: FastAPI) -> AsyncGenerator: # Initialize disposable email checker and store in app.state app.state.email_checker = None - if app.state.redis is not None: - try: - email_checker = EmailChecker(app.state.redis) - await email_checker.initialize() - app.state.email_checker = email_checker - except (RuntimeError, ValueError, ConnectionError) as e: - logger.warning("Failed to initialize email checker: %s", e) + try: + email_checker = EmailChecker(app.state.redis) + await email_checker.initialize() + app.state.email_checker = email_checker + except (RuntimeError, ValueError, ConnectionError) as e: + logger.warning("Failed to initialize email checker: %s", e) logger.info("Application startup complete") From a0e45336bf32b4f856b0e7c4e4558582db447b1a Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Sat, 8 Nov 2025 11:04:20 +0100 Subject: [PATCH 36/79] fix(cicd): Lint pre-commit yaml and update mdformat pre-commit hook --- .pre-commit-config.yaml | 92 ++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 48 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6b9ca26..a6b2c07 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,85 +5,81 @@ repos: ### Global hooks -- repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update + - repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update rev: v0.9.0 hooks: - - id: pre-commit-update # Autoupdate pre-commit hooks - # TODO: Re-add mdformat to pre-commit-update when mdformat plugins are compatible with mdformat 1.0.0 - args: [--exclude, mdformat] + - id: pre-commit-update # Autoupdate pre-commit hooks -- repo: https://github.com/gitleaks/gitleaks + - repo: https://github.com/gitleaks/gitleaks rev: v8.29.0 hooks: - - id: gitleaks + - id: gitleaks -- repo: https://github.com/executablebooks/mdformat - rev: 0.7.22 + - repo: https://github.com/executablebooks/mdformat + rev: 1.0.0 hooks: - - id: mdformat # Format Markdown files. + - id: mdformat # Format Markdown files. additional_dependencies: - - mdformat-gfm # Support GitHub Flavored Markdown. - - mdformat-footnote - - mdformat-frontmatter - - mdformat-ruff # Support Python code blocks linted with Ruff. + - mdformat-gfm>=1.0.0 # Support GitHub Flavored Markdown. + - mdformat-ruff # Support Python code blocks linted with Ruff. -- repo: https://github.com/pre-commit/pre-commit-hooks + - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - - id: check-added-large-files - - id: check-case-conflict # Check for files with names that differ only in case. - - id: check-executables-have-shebangs - - id: check-shebang-scripts-are-executable - - id: check-toml - - id: check-yaml + - id: check-added-large-files + - id: check-case-conflict # Check for files with names that differ only in case. + - id: check-executables-have-shebangs + - id: check-shebang-scripts-are-executable + - id: check-toml + - id: check-yaml exclude: ^docs/mkdocs.yml$ # Exclude mkdocs.yml because it uses an obscure tag to allow for mermaid formatting - - id: detect-private-key - - id: end-of-file-fixer # Ensure files end with a newline. - - id: mixed-line-ending - - id: no-commit-to-branch # Prevent commits to main and master branches. - - id: trailing-whitespace + - id: detect-private-key + - id: end-of-file-fixer # Ensure files end with a newline. + - id: mixed-line-ending + - id: no-commit-to-branch # Prevent commits to main and master branches. + - id: trailing-whitespace args: ["--markdown-linebreak-ext", "md"] # Preserve Markdown hard line breaks. -- repo: https://github.com/commitizen-tools/commitizen + - repo: https://github.com/commitizen-tools/commitizen rev: v4.9.1 hooks: - - id: commitizen + - id: commitizen stages: [commit-msg] -- repo: https://github.com/simonvanlierde/check-json5 + - repo: https://github.com/simonvanlierde/check-json5 rev: v1.1.0 hooks: - - id: check-json5 + - id: check-json5 files: ^ (?!(backend/frontend-app|frontend-web)/data/) ### Backend hooks -- repo: https://github.com/RobertCraigie/pyright-python # Lint backend code with Pyright. + - repo: https://github.com/RobertCraigie/pyright-python # Lint backend code with Pyright. rev: v1.1.407 hooks: - - id: pyright + - id: pyright files: ^backend/(app|scripts|tests)/ entry: pyright --project backend -- repo: https://github.com/astral-sh/ruff-pre-commit + - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.14.4 hooks: - - id: ruff-check # Lint code + - id: ruff-check # Lint code files: ^backend/(app|scripts|tests)/ args: ["--fix", "--config", "backend/pyproject.toml", "--ignore", "FIX002"] # Allow TODO comments in commits. - - id: ruff-format # Format code + - id: ruff-format # Format code files: ^backend/(app|scripts|tests)/ args: ["--config", "backend/pyproject.toml"] -- repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.7 + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.9.8 hooks: - - id: uv-lock # Update the uv lockfile for the backend. + - id: uv-lock # Update the uv lockfile for the backend. files: ^backend/(uv\.lock|pyproject\.toml|uv\.toml)$ entry: uv lock --project backend -- repo: local + - repo: local hooks: # Check if Alembic migrations are up-to-date. Uses uv to ensure the right environment when executed through VS Code Git extension. - - id: backend-alembic-autogen-check + - id: backend-alembic-autogen-check name: check alembic migrations entry: bash -c 'cd backend && uv run alembic-autogen-check' language: system @@ -92,21 +88,21 @@ repos: stages: [pre-commit] ### Frontend hooks -- repo: local + - repo: local hooks: - - id: frontend-web-format + - id: frontend-web-format name: format frontend-web code entry: bash -c 'cd frontend-web && npm run format' - language: system + language: + system # Match frontend JavaScript and TypeScript files for formatting. - files: - ^frontend-web\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ + files: ^frontend-web\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ pass_filenames: false - - id: frontend-app-format + - id: frontend-app-format name: format frontend-app code entry: bash -c 'cd frontend-app && npm run format' - language: system + language: + system # Match frontend JavaScript and TypeScript files for formatting. - files: - ^frontend-app\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ + files: ^frontend-app\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ pass_filenames: false From 9bbd55654e26d15db20cf44b74f43a1eb85a30cd Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Sat, 8 Nov 2025 10:21:03 +0000 Subject: [PATCH 37/79] fix(backend): Move back to python 3.13 because Dockerized asyncpg doesn't support it yet --- backend/.python-version | 2 +- backend/Dockerfile | 5 +++-- backend/Dockerfile.dev | 2 +- backend/Dockerfile.migrations | 4 ++-- backend/pyproject.toml | 1 + codemeta.json | 2 +- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/backend/.python-version b/backend/.python-version index 6324d40..24ee5b1 100644 --- a/backend/.python-version +++ b/backend/.python-version @@ -1 +1 @@ -3.14 +3.13 diff --git a/backend/Dockerfile b/backend/Dockerfile index 17b19a7..15ca725 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,5 +1,6 @@ # --- Builder stage --- -FROM ghcr.io/astral-sh/uv:0.9-python3.14-trixie-slim AS builder +# TODO: Move to Python 3.14 once asyncpg supports it (version 0.31.0+, see https://github.com/MagicStack/asyncpg/issues/1282) +FROM ghcr.io/astral-sh/uv:0.9-python3.13-trixie-slim AS builder # Install git for custom dependencies (fastapi-users-db-sqlmodel) RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ @@ -34,7 +35,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --locked --no-editable --no-default-groups --group=api # --- Final runtime stage --- -FROM python:3.14-slim +FROM python:3.13-slim # Build arguments ARG WORKDIR=/opt/relab/backend diff --git a/backend/Dockerfile.dev b/backend/Dockerfile.dev index 36cea79..5609289 100644 --- a/backend/Dockerfile.dev +++ b/backend/Dockerfile.dev @@ -1,6 +1,6 @@ # Development Dockerfile for FastAPI Backend # Note: This requires mounting the source code as a volume in docker-compose.override.yml -FROM ghcr.io/astral-sh/uv:0.9-python3.14-trixie-slim +FROM ghcr.io/astral-sh/uv:0.9-python3.13-trixie-slim # Build arguments ARG WORKDIR=/opt/relab/backend diff --git a/backend/Dockerfile.migrations b/backend/Dockerfile.migrations index 73a3932..6c03c35 100644 --- a/backend/Dockerfile.migrations +++ b/backend/Dockerfile.migrations @@ -1,5 +1,5 @@ # --- Builder stage --- -FROM ghcr.io/astral-sh/uv:0.9-python3.14-trixie-slim AS builder +FROM ghcr.io/astral-sh/uv:0.9-python3.13-trixie-slim AS builder WORKDIR /opt/relab/backend_migrations @@ -33,7 +33,7 @@ COPY scripts/ scripts/ COPY app/ app/ # --- Final runtime stage --- -FROM python:3.14-slim +FROM python:3.13-slim # Build arguments ARG WORKDIR=/opt/relab/backend_migrations diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 860929e..042eaca 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -23,6 +23,7 @@ ## Dependencies and version constraints dependencies = [ "asyncache>=0.3.1", + # TODO: Move to Python 3.14 once asyncpg supports it (version 0.31.0+, see https://github.com/MagicStack/asyncpg/issues/1282) "asyncpg>=0.30.0", "cachetools>=5.5.2", "email-validator>=2.2.0", diff --git a/codemeta.json b/codemeta.json index f3704e5..9d940b3 100644 --- a/codemeta.json +++ b/codemeta.json @@ -42,7 +42,7 @@ ], "license": "https://spdx.org/licenses/AGPL-3.0-or-later", "name": "Reverse Engineering Lab", - "programmingLanguage": ["Python 3.14", "JavaScript"], + "programmingLanguage": ["Python 3.13", "JavaScript"], "softwareRequirements": "Docker", "version": "0.1.0" } From 7ed08d93880d2a5061181077ef82f153caeff177 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 09:50:23 +0000 Subject: [PATCH 38/79] chore(deps): update infrastructure --- compose.prod.yml | 2 +- compose.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compose.prod.yml b/compose.prod.yml index e73373c..866eb99 100644 --- a/compose.prod.yml +++ b/compose.prod.yml @@ -36,7 +36,7 @@ services: - cache_data:/data cloudflared: # Cloudflared tunnel to cml-relab.org - image: cloudflare/cloudflared:latest@sha256:89ee50efb1e9cb2ae30281a8a404fed95eb8f02f0a972617526f8c5b417acae2 + image: cloudflare/cloudflared:latest@sha256:396cd2e6f021275ad09969a1b4f1a7e62ca5349fde62781ce082bb2c18105c70 command: tunnel --no-autoupdate run env_file: .env # Should contain TUNNEL_TOKEN variable pull_policy: always diff --git a/compose.yml b/compose.yml index 8937ad6..1975b5f 100644 --- a/compose.yml +++ b/compose.yml @@ -36,7 +36,7 @@ services: - user_uploads:/opt/relab/backend/data/uploads database: - image: postgres:18@sha256:41bfa2e5b168fff0847a5286694a86cff102bdc4d59719869f6b117bb30b3a24 + image: postgres:18@sha256:78f1aed6e8c0185d3c6963c8343dd018c63e8ba5ebd78c159c7c772a05e75b30 env_file: ./backend/.env healthcheck: test: ["CMD-SHELL", "pg_isready -h localhost -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] @@ -57,7 +57,7 @@ services: restart: unless-stopped docs: - image: squidfunk/mkdocs-material:9@sha256:980e11fed03b8e7851e579be9f34b1210f516c9f0b4da1a1457f21a460bd6628 + image: squidfunk/mkdocs-material:9@sha256:f5c556a6d30ce0c1c0df10e3c38c79bbcafdaea4b1c1be366809d0d4f6f9d57f restart: unless-stopped volumes: - ./docs:/docs From ec74a11cf6f49958ce8dfe654078c2bf10f5289c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 08:13:49 +0000 Subject: [PATCH 39/79] fix(deps): update expo monorepo --- frontend-app/package-lock.json | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/frontend-app/package-lock.json b/frontend-app/package-lock.json index bcce7aa..ccf4078 100644 --- a/frontend-app/package-lock.json +++ b/frontend-app/package-lock.json @@ -101,6 +101,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -3847,6 +3848,7 @@ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.18.tgz", "integrity": "sha512-DZgd6860dxcq3YX7UzIXeBr6m3UgXvo9acxp5jiJyIZXdR00Br9JwVkO7e0bUeTA2d3Z8dsmtAR84Y86NnH64Q==", "license": "MIT", + "peer": true, "dependencies": { "@react-navigation/core": "^7.12.4", "escape-string-regexp": "^4.0.0", @@ -4043,6 +4045,7 @@ "integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -4121,6 +4124,7 @@ "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/types": "8.45.0", @@ -4683,6 +4687,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5374,6 +5379,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -6630,6 +6636,7 @@ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -6724,6 +6731,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -6843,6 +6851,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7156,6 +7165,7 @@ "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.15.tgz", "integrity": "sha512-d4OLUz/9nC+Aw00zamHANh5TZB4/YVYvSmKJAvCfLNxOY2AJeTFAvk0mU5HwICeHQBp6zHtz13DDCiMbcyVQWQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "54.0.12", @@ -7254,6 +7264,7 @@ "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.9.tgz", "integrity": "sha512-sqoXHAOGDcr+M9NlXzj1tGoZyd3zxYDy215W6E0Z0n8fgBaqce9FAYQE2bu5X4G629AYig5go7U6sQz7Pjcm8A==", "license": "MIT", + "peer": true, "dependencies": { "@expo/config": "~12.0.9", "@expo/env": "~2.0.7" @@ -7278,6 +7289,7 @@ "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.9.tgz", "integrity": "sha512-xCoQbR/36qqB6tew/LQ6GWICpaBmHLhg/Loix5Rku/0ZtNaXMJv08M9o1AcrdiGTn/Xf/BnLu6DgS45cWQEHZg==", "license": "MIT", + "peer": true, "dependencies": { "fontfaceobserver": "^2.1.0" }, @@ -7372,6 +7384,7 @@ "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-8.0.8.tgz", "integrity": "sha512-MyeMcbFDKhXh4sDD1EHwd0uxFQNAc6VCrwBkNvvvufUsTYFq3glTA9Y8a+x78CPpjNqwNAamu74yIaIz7IEJyg==", "license": "MIT", + "peer": true, "dependencies": { "expo-constants": "~18.0.8", "invariant": "^2.2.4" @@ -11187,6 +11200,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -11416,6 +11430,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -11435,6 +11450,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -11471,6 +11487,7 @@ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.4.tgz", "integrity": "sha512-bt5bz3A/+Cv46KcjV0VQa+fo7MKxs17RCcpzjftINlen4ZDUl0I6Ut+brQ2FToa5oD0IB0xvQHfmsg2EDqsZdQ==", "license": "MIT", + "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.81.4", @@ -11528,6 +11545,7 @@ "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.28.0.tgz", "integrity": "sha512-0msfJ1vRxXKVgTgvL+1ZOoYw3/0z1R+Ked0+udoJhyplC2jbVKIJ8Z1bzWdpQRCV3QcQ87Op0zJVE5DhKK2A0A==", "license": "MIT", + "peer": true, "dependencies": { "@egjs/hammerjs": "^2.0.17", "hoist-non-react-statics": "^3.3.0", @@ -11612,6 +11630,7 @@ "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.1.3.tgz", "integrity": "sha512-GP8wsi1u3nqvC1fMab/m8gfFwFyldawElCcUSBJQgfrXeLmsPPUOpDw44lbLeCpcwUuLa05WTVePdTEwCLTUZg==", "license": "MIT", + "peer": true, "dependencies": { "react-native-is-edge-to-edge": "^1.2.1", "semver": "7.7.2" @@ -11640,6 +11659,7 @@ "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz", "integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==", "license": "MIT", + "peer": true, "peerDependencies": { "react": "*", "react-native": "*" @@ -11650,6 +11670,7 @@ "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.16.0.tgz", "integrity": "sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==", "license": "MIT", + "peer": true, "dependencies": { "react-freeze": "^1.0.0", "react-native-is-edge-to-edge": "^1.2.1", @@ -11680,6 +11701,7 @@ "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.2.tgz", "integrity": "sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", @@ -11712,6 +11734,7 @@ "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.15.0.tgz", "integrity": "sha512-Vzjgy8mmxa/JO6l5KZrsTC7YemSdq+qB01diA0FqjUTaWGAGwuykpJ73MDj3+mzBSlaDxAEugHzTtkUQkQEQeQ==", "license": "MIT", + "peer": true, "dependencies": { "escape-string-regexp": "^4.0.0", "invariant": "2.2.4" @@ -11726,6 +11749,7 @@ "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.5.1.tgz", "integrity": "sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w==", "license": "MIT", + "peer": true, "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", @@ -11813,6 +11837,7 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -13251,6 +13276,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -13457,6 +13483,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14247,6 +14274,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } From dadebaac0942d0af6eea2f5b4139dea5459e0c0d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 02:31:30 +0000 Subject: [PATCH 40/79] chore(deps): update dependency @react-navigation/bottom-tabs to v7.4.9 --- frontend-app/package-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend-app/package-lock.json b/frontend-app/package-lock.json index ccf4078..7e7203f 100644 --- a/frontend-app/package-lock.json +++ b/frontend-app/package-lock.json @@ -1483,6 +1483,7 @@ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } From 086f963d10a15c1585b0fd777fe01137107a176d Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Sat, 8 Nov 2025 11:48:24 +0100 Subject: [PATCH 41/79] fix(backend): Use TypeAlias over simple type declaration for custom schema field types --- backend/app/api/common/schemas/custom_fields.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/app/api/common/schemas/custom_fields.py b/backend/app/api/common/schemas/custom_fields.py index c50df7a..78019ec 100644 --- a/backend/app/api/common/schemas/custom_fields.py +++ b/backend/app/api/common/schemas/custom_fields.py @@ -1,13 +1,13 @@ """Shared fields for DTO schemas.""" -from typing import Annotated +from typing import Annotated, TypeAlias from pydantic import AnyUrl, HttpUrl, PlainSerializer, StringConstraints # HTTP URL that is stored as string in the database. -type HttpUrlToDB = Annotated[ +HttpUrlToDB: TypeAlias = Annotated[ HttpUrl, PlainSerializer(lambda x: str(x), return_type=str), StringConstraints(max_length=250) ] -type AnyUrlToDB = Annotated[ +AnyUrlToDB: TypeAlias = Annotated[ AnyUrl, PlainSerializer(lambda x: str(x), return_type=str), StringConstraints(max_length=250) ] From b4f3fb66960f1a1a3edc8b9ed9a6f0c4f499cd13 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Sat, 8 Nov 2025 11:48:57 +0100 Subject: [PATCH 42/79] fix(backend): Only apply sorting if explicit sorting was requested --- backend/app/api/common/crud/base.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/app/api/common/crud/base.py b/backend/app/api/common/crud/base.py index f183630..9b3e768 100644 --- a/backend/app/api/common/crud/base.py +++ b/backend/app/api/common/crud/base.py @@ -95,8 +95,10 @@ def get_models_query( statement = add_filter_joins(statement, model, model_filter) # Apply the filter statement = model_filter.filter(statement) - # Apply sorting - fastapi-filter stores it but doesn't apply it automatically - statement = model_filter.sort(statement) + # Apply sorting if specified + # HACK: Inspect sort vars to see if any sorting is defined + if vars(model_filter.sort): + statement = model_filter.sort(statement) relationships_to_exclude = [] statement, relationships_to_exclude = add_relationship_options( From 3325db7da667697412eb72093972007d6d3274c3 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Sat, 8 Nov 2025 12:03:07 +0100 Subject: [PATCH 43/79] feature(backend): Mask user emails in logs for privacy --- .../app/api/auth/utils/programmatic_emails.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/backend/app/api/auth/utils/programmatic_emails.py b/backend/app/api/auth/utils/programmatic_emails.py index 97bee35..53f4028 100644 --- a/backend/app/api/auth/utils/programmatic_emails.py +++ b/backend/app/api/auth/utils/programmatic_emails.py @@ -22,7 +22,21 @@ def generate_token_link(token: str, route: str, base_url: str | AnyUrl | None = base_url = str(core_settings.frontend_app_url) return urljoin(str(base_url), f"{route}?token={token}") +def mask_email_for_log(email: EmailStr, mask: bool = True, max_len: int = 80) -> str: + """Mask emails for logging. + + Also remove non-printable characters and truncates long domains. + """ + string = "".join(ch for ch in str(email) if ch.isprintable()) + local, sep, domain = string.partition("@") + if sep and mask: + masked = (f"{local[0]}***@{domain}" if len(local) > 1 else f"*@{domain}") + else: + masked = string + return (f"{masked[:max_len-3]}..." if len(masked) > max_len else masked) + +### Generic email function ### async def send_email_with_template( to_email: EmailStr, subject: str, @@ -48,10 +62,10 @@ async def send_email_with_template( if background_tasks: background_tasks.add_task(fm.send_message, message, template_name=template_name) - logger.info("Email queued for background sending to %s using template %s", to_email, template_name) + logger.info("Email queued for background sending to %s using template %s", mask_email_for_log(to_email), template_name) else: await fm.send_message(message, template_name=template_name) - logger.info("Email sent to %s using template %s", to_email, template_name) + logger.info("Email sent to %s using template %s", mask_email_for_log(to_email), template_name) ### Authentication email functions ### From 1511004532af038e336f64bea2335a8e25855c65 Mon Sep 17 00:00:00 2001 From: Simon van Lierde <90462640+simonvanlierde@users.noreply.github.com> Date: Sat, 8 Nov 2025 12:06:30 +0100 Subject: [PATCH 44/79] Potential fix for code scanning alert no. 8: Log Injection Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- backend/app/api/auth/utils/programmatic_emails.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/app/api/auth/utils/programmatic_emails.py b/backend/app/api/auth/utils/programmatic_emails.py index 53f4028..b5217f8 100644 --- a/backend/app/api/auth/utils/programmatic_emails.py +++ b/backend/app/api/auth/utils/programmatic_emails.py @@ -25,9 +25,10 @@ def generate_token_link(token: str, route: str, base_url: str | AnyUrl | None = def mask_email_for_log(email: EmailStr, mask: bool = True, max_len: int = 80) -> str: """Mask emails for logging. - Also remove non-printable characters and truncates long domains. + Also remove non-printable characters and truncates long domains. Explicitly removes log-breaking control characters. """ - string = "".join(ch for ch in str(email) if ch.isprintable()) + # Remove non-printable and log-breaking control characters + string = "".join(ch for ch in str(email) if ch.isprintable()).replace('\n', '').replace('\r', '') local, sep, domain = string.partition("@") if sep and mask: masked = (f"{local[0]}***@{domain}" if len(local) > 1 else f"*@{domain}") From 7a43515ed9049722b96dc8a9de4e0e145cfb3ece Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Sat, 8 Nov 2025 12:13:58 +0100 Subject: [PATCH 45/79] fix(backend): Simplify URL serializing to DB --- backend/app/api/common/schemas/custom_fields.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/app/api/common/schemas/custom_fields.py b/backend/app/api/common/schemas/custom_fields.py index 78019ec..5614bd2 100644 --- a/backend/app/api/common/schemas/custom_fields.py +++ b/backend/app/api/common/schemas/custom_fields.py @@ -6,8 +6,8 @@ # HTTP URL that is stored as string in the database. HttpUrlToDB: TypeAlias = Annotated[ - HttpUrl, PlainSerializer(lambda x: str(x), return_type=str), StringConstraints(max_length=250) + HttpUrl, PlainSerializer(str, return_type=str), StringConstraints(max_length=250) ] AnyUrlToDB: TypeAlias = Annotated[ - AnyUrl, PlainSerializer(lambda x: str(x), return_type=str), StringConstraints(max_length=250) + AnyUrl, PlainSerializer(str, return_type=str), StringConstraints(max_length=250) ] From db2e6e51f21361f59850ec6dadd7bed0e97cc339 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Sat, 8 Nov 2025 12:27:20 +0100 Subject: [PATCH 46/79] fix(backend): Simplify dummy data checking logic --- backend/scripts/seed/migrations_entrypoint.sh | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/backend/scripts/seed/migrations_entrypoint.sh b/backend/scripts/seed/migrations_entrypoint.sh index 4124d6e..aeb72b7 100755 --- a/backend/scripts/seed/migrations_entrypoint.sh +++ b/backend/scripts/seed/migrations_entrypoint.sh @@ -32,15 +32,20 @@ if [ "$(lc "$SEED_TAXONOMIES")" = "true" ]; then .venv/bin/python -m scripts.seed.taxonomies.harmonized_system fi -# Check if all tables are empty -echo "Checking if all tables in the database are empty using scripts/db_is_empty.py..." -DB_EMPTY=$(.venv/bin/python -m scripts.db_is_empty) - -if [ "$(lc "$DB_EMPTY")" = "true" ] && [ "$(lc "$SEED_DUMMY_DATA")" = "true" ]; then - echo "All tables are empty, proceeding to seed dummy data..." - .venv/bin/python -m scripts.seed.dummy_data +# Seed dummy data if enabled and if the database is empty +if [ "$(lc "$SEED_DUMMY_DATA")" = "true" ]; then + echo "Dummy data seeding is enabled." + echo "Checking if all tables in the database are empty using scripts/db_is_empty.py..." + DB_EMPTY=$(.venv/bin/python -m scripts.db_is_empty) + + if [ "$(lc "$DB_EMPTY")" = "true" ]; then + echo "All tables are empty, proceeding to seed dummy data..." + .venv/bin/python -m scripts.seed.dummy_data + else + echo "Database already has data seeding disabled, skipping." + fi else - echo "Database already has data or dummy data seeding disabled, skipping." + echo "Dummy data seeding is disabled." fi # Create a superuser if the required environment variables are set From fbb34dd9209f7475e4f72b2d1a03f3770481dd1b Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Sat, 8 Nov 2025 12:30:08 +0100 Subject: [PATCH 47/79] fix(backend): Spellcheck --- backend/app/core/redis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/core/redis.py b/backend/app/core/redis.py index ea99feb..a8a0973 100644 --- a/backend/app/core/redis.py +++ b/backend/app/core/redis.py @@ -101,7 +101,7 @@ async def set_redis_value(redis_client: Redis, key: str, value: Any, ex: int | N Args: redis_client: Redis client key: Redis key - value: Value to stores + value: Value to store ex: Expiration time in seconds (optional) Returns: From fffbff3c20426db6880f00e7e35896ab545707a5 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 17 Nov 2025 13:38:00 +0100 Subject: [PATCH 48/79] feat(backend): Add circularity_properties model to products - Add CircularityPropertiesBase and CircularityProperties database models with fields for recyclability, repairability, and remanufacturability - Each property has observation (required), comment (optional), and reference (optional) fields - Add one-to-one relationship between Product and CircularityProperties - Create full CRUD operations for circularity properties - Add REST API endpoints (GET, POST, PATCH, DELETE) for managing circularity properties - Update product schemas to support circularity_properties in create/read/update operations - Update API documentation and examples to include circularity properties - Add alembic migration script for new circularity properties model --- ..._add_basic_circularity_properties_model.py | 54 +++++++++++ backend/app/api/data_collection/crud.py | 97 ++++++++++++++++++- backend/app/api/data_collection/models.py | 32 ++++++ backend/app/api/data_collection/routers.py | 73 +++++++++++++- backend/app/api/data_collection/schemas.py | 71 ++++++++++++++ 5 files changed, 318 insertions(+), 9 deletions(-) create mode 100644 backend/alembic/versions/b43d157d07f1_add_basic_circularity_properties_model.py diff --git a/backend/alembic/versions/b43d157d07f1_add_basic_circularity_properties_model.py b/backend/alembic/versions/b43d157d07f1_add_basic_circularity_properties_model.py new file mode 100644 index 0000000..0e5a745 --- /dev/null +++ b/backend/alembic/versions/b43d157d07f1_add_basic_circularity_properties_model.py @@ -0,0 +1,54 @@ +"""Add basic circularity_properties model + +Revision ID: b43d157d07f1 +Revises: 95cc94317b69 +Create Date: 2025-11-17 13:30:07.435637 + +""" + +from collections.abc import Sequence +from typing import Union + +import sqlalchemy as sa +import sqlmodel + +import app.api.common.models.custom_types +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "b43d157d07f1" +down_revision: str | None = "95cc94317b69" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "circularityproperties", + sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), + sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), + sa.Column("recyclability_observation", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=False), + sa.Column("recyclability_comment", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), + sa.Column("recyclability_reference", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), + sa.Column("repairability_observation", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=False), + sa.Column("repairability_comment", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), + sa.Column("repairability_reference", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), + sa.Column("remanufacturability_observation", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=False), + sa.Column("remanufacturability_comment", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), + sa.Column("remanufacturability_reference", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("product_id", sa.Integer(), nullable=False), + sa.ForeignKeyConstraint( + ["product_id"], + ["product.id"], + ), + sa.PrimaryKeyConstraint("id"), + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("circularityproperties") + # ### end Alembic commands ### diff --git a/backend/app/api/data_collection/crud.py b/backend/app/api/data_collection/crud.py index 52d7570..cc9e20e 100644 --- a/backend/app/api/data_collection/crud.py +++ b/backend/app/api/data_collection/crud.py @@ -29,8 +29,10 @@ MaterialProductLinkUpdate, ) from app.api.data_collection.filters import ProductFilterWithRelationships -from app.api.data_collection.models import PhysicalProperties, Product +from app.api.data_collection.models import CircularityProperties, PhysicalProperties, Product from app.api.data_collection.schemas import ( + CircularityPropertiesCreate, + CircularityPropertiesUpdate, ComponentCreateWithComponents, PhysicalPropertiesCreate, PhysicalPropertiesUpdate, @@ -122,6 +124,72 @@ async def delete_physical_properties(db: AsyncSession, product: Product) -> None await db.commit() +### CircularityProperty CRUD operations ### +async def get_circularity_properties(db: AsyncSession, product_id: int) -> CircularityProperties: + """Get circularity properties for a product.""" + product: Product = await db_get_model_with_id_if_it_exists(db, Product, product_id) + + if not product.circularity_properties: + err_msg: str = f"Circularity properties for product with id {product_id} not found" + raise ValueError(err_msg) + + return product.circularity_properties + + +async def create_circularity_properties( + db: AsyncSession, + circularity_properties: CircularityPropertiesCreate, + product_id: int, +) -> CircularityProperties: + """Create circularity properties for a product.""" + # Validate that product exists and doesn't have circularity properties + product: Product = await db_get_model_with_id_if_it_exists(db, Product, product_id) + if product.circularity_properties: + err_msg: str = f"Product with id {product_id} already has circularity properties" + raise ValueError(err_msg) + + # Create circularity properties + db_circularity_property = CircularityProperties( + **circularity_properties.model_dump(), + product_id=product_id, + ) + db.add(db_circularity_property) + await db.commit() + await db.refresh(db_circularity_property) + + return db_circularity_property + + +async def update_circularity_properties( + db: AsyncSession, product_id: int, circularity_properties: CircularityPropertiesUpdate +) -> CircularityProperties: + """Update circularity properties for a product.""" + # Validate that product exists and has circularity properties + product: Product = await db_get_model_with_id_if_it_exists(db, Product, product_id) + if not (db_circularity_properties := product.circularity_properties): + err_msg: EmailStr = f"Circularity properties for product with id {product_id} not found" + raise ValueError(err_msg) + + circularity_properties_data: dict[str, Any] = circularity_properties.model_dump(exclude_unset=True) + db_circularity_properties.sqlmodel_update(circularity_properties_data) + + db.add(db_circularity_properties) + await db.commit() + await db.refresh(db_circularity_properties) + return db_circularity_properties + + +async def delete_circularity_properties(db: AsyncSession, product: Product) -> None: + """Delete circularity properties for a product.""" + # Validate that product exists and has circularity properties + if not (db_circularity_properties := product.circularity_properties): + err_msg: EmailStr = f"Circularity properties for product with id {product.id} not found" + raise ValueError(err_msg) + + await db.delete(db_circularity_properties) + await db.commit() + + ### Product CRUD operations ### ## Basic CRUD operations ### async def get_product_trees( @@ -178,6 +246,7 @@ async def create_component( "components", "owner_id", "physical_properties", + "circularity_properties", "videos", "bill_of_materials", } @@ -198,6 +267,13 @@ async def create_component( ) db.add(db_physical_property) + if component.circularity_properties: + db_circularity_property = CircularityProperties( + **component.circularity_properties.model_dump(), + product_id=db_component.id, # pyright: ignore[reportArgumentType] # component ID is guaranteed by database flush above + ) + db.add(db_circularity_property) + # Create videos if component.videos: for video in component.videos: @@ -257,6 +333,7 @@ async def create_product( exclude={ "components", "physical_properties", + "circularity_properties", "videos", "bill_of_materials", } @@ -274,6 +351,13 @@ async def create_product( ) db.add(db_physical_properties) + if product.circularity_properties: + db_circularity_properties = CircularityProperties( + **product.circularity_properties.model_dump(), + product_id=db_product.id, # pyright: ignore[reportArgumentType] # product ID is guaranteed by database flush above + ) + db.add(db_circularity_properties) + # Create videos if product.videos: for video in product.videos: @@ -327,12 +411,17 @@ async def update_product( if product.product_type_id: await db_get_model_with_id_if_it_exists(db, ProductType, product.product_type_id) - product_data: dict[str, Any] = product.model_dump(exclude_unset=True, exclude={"physical_properties"}) + product_data: dict[str, Any] = product.model_dump( + exclude_unset=True, exclude={"physical_properties", "circularity_properties"} + ) db_product.sqlmodel_update(product_data) # Update properties - if isinstance(product, ProductUpdateWithProperties) and product.physical_properties: - await update_physical_properties(db, product_id, product.physical_properties) + if isinstance(product, ProductUpdateWithProperties): + if product.physical_properties: + await update_physical_properties(db, product_id, product.physical_properties) + if product.circularity_properties: + await update_circularity_properties(db, product_id, product.circularity_properties) db.add(db_product) await db.commit() diff --git a/backend/app/api/data_collection/models.py b/backend/app/api/data_collection/models.py index 4cb5388..c96f59c 100644 --- a/backend/app/api/data_collection/models.py +++ b/backend/app/api/data_collection/models.py @@ -61,6 +61,35 @@ class PhysicalProperties(PhysicalPropertiesBase, TimeStampMixinBare, table=True) product: "Product" = Relationship(back_populates="physical_properties") +class CircularityPropertiesBase(CustomBase): + """Base model to store circularity properties of a product.""" + + # Recyclability + recyclability_observation: str = Field(min_length=2, max_length=500) + recyclability_comment: str | None = Field(default=None, max_length=100) + recyclability_reference: str | None = Field(default=None, max_length=100) + + # Repairability + repairability_observation: str = Field(min_length=2, max_length=500) + repairability_comment: str | None = Field(default=None, max_length=100) + repairability_reference: str | None = Field(default=None, max_length=100) + + # Remanufacturability + remanufacturability_observation: str = Field(min_length=2, max_length=500) + remanufacturability_comment: str | None = Field(default=None, max_length=100) + remanufacturability_reference: str | None = Field(default=None, max_length=100) + + +class CircularityProperties(CircularityPropertiesBase, TimeStampMixinBare, table=True): + """Model to store circularity properties of a product.""" + + id: int | None = Field(default=None, primary_key=True) + + # One-to-one relationships + product_id: int = Field(foreign_key="product.id") + product: "Product" = Relationship(back_populates="circularity_properties") + + ### Product Model ### class ProductBase(CustomBase): """Basic model to store product information.""" @@ -115,6 +144,9 @@ class Product(ProductBase, TimeStampMixinBare, table=True): physical_properties: PhysicalProperties | None = Relationship( back_populates="product", cascade_delete=True, sa_relationship_kwargs={"uselist": False, "lazy": "selectin"} ) + circularity_properties: CircularityProperties | None = Relationship( + back_populates="product", cascade_delete=True, sa_relationship_kwargs={"uselist": False, "lazy": "selectin"} + ) # Many-to-one relationships files: list["File"] | None = Relationship(back_populates="product", cascade_delete=True) diff --git a/backend/app/api/data_collection/routers.py b/backend/app/api/data_collection/routers.py index cd4ebbb..50cc836 100644 --- a/backend/app/api/data_collection/routers.py +++ b/backend/app/api/data_collection/routers.py @@ -45,10 +45,14 @@ get_user_owned_product_id, ) from app.api.data_collection.models import ( + CircularityProperties, PhysicalProperties, Product, ) from app.api.data_collection.schemas import ( + CircularityPropertiesCreate, + CircularityPropertiesRead, + CircularityPropertiesUpdate, ComponentCreateWithComponents, ComponentReadWithRecursiveComponents, PhysicalPropertiesCreate, @@ -116,13 +120,14 @@ async def get_user_products( description="Relationships to include", openapi_examples={ "none": {"value": {}}, - "properties": {"value": {"physical_properties"}}, + "properties": {"value": {"physical_properties", "circularity_properties"}}, "materials": {"value": {"bill_of_materials"}}, "components": {"value": {"components"}}, "media": {"value": {"images", "videos", "files"}}, "all": { "value": { "physical_properties", + "circularity_properties", "images", "videos", "files", @@ -187,13 +192,14 @@ async def get_products( description="Relationships to include", openapi_examples={ "none": {"value": []}, - "properties": {"value": ["physical_properties"]}, + "properties": {"value": ["physical_properties", "circularity_properties"]}, "materials": {"value": ["bill_of_materials"]}, "media": {"value": ["images", "videos", "files"]}, "components": {"value": ["components"]}, "all": { "value": [ "physical_properties", + "circularity_properties", "images", "videos", "files", @@ -215,6 +221,7 @@ async def get_products( Relationships that can be included: - physical_properties: Physical measurements and attributes + - circularity_properties: Circularity properties (recyclability, repairability, remanufacturability) - images: Product images - videos: Product videos - files: Related documents @@ -324,13 +331,14 @@ async def get_product( description="Relationships to include", openapi_examples={ "none": {"value": []}, - "properties": {"value": ["physical_properties"]}, + "properties": {"value": ["physical_properties", "circularity_properties"]}, "materials": {"value": ["bill_of_materials"]}, "media": {"value": ["images", "videos", "files"]}, "components": {"value": ["components"]}, "all": { "value": [ "physical_properties", + "circularity_properties", "images", "videos", "files", @@ -347,6 +355,7 @@ async def get_product( Relationships that can be included: - physical_properties: Physical measurements and attributes + - circularity_properties: Circularity properties (recyclability, repairability, remanufacturability) - images: Product images - videos: Product videos - files: Related documents @@ -564,13 +573,14 @@ async def get_product_components( description="Relationships to include", openapi_examples={ "none": {"value": []}, - "properties": {"value": ["physical_properties"]}, + "properties": {"value": ["physical_properties", "circularity_properties"]}, "materials": {"value": ["bill_of_materials"]}, "media": {"value": ["images", "videos", "files"]}, "components": {"value": ["components"]}, "all": { "value": [ "physical_properties", + "circularity_properties", "images", "videos", "files", @@ -612,13 +622,14 @@ async def get_product_component( description="Relationships to include", openapi_examples={ "none": {"value": []}, - "properties": {"value": ["physical_properties"]}, + "properties": {"value": ["physical_properties", "circularity_properties"]}, "materials": {"value": ["bill_of_materials"]}, "media": {"value": ["images", "videos", "files"]}, "components": {"value": ["components"]}, "all": { "value": [ "physical_properties", + "circularity_properties", "images", "videos", "files", @@ -773,6 +784,58 @@ async def delete_product_physical_properties( await crud.delete_physical_properties(session, product) +@product_router.get( + "/{product_id}/circularity_properties", + response_model=CircularityPropertiesRead, + summary="Get product circularity properties", +) +async def get_product_circularity_properties(product_id: PositiveInt, session: AsyncSessionDep) -> CircularityProperties: + """Get circularity properties for a product.""" + return await crud.get_circularity_properties(session, product_id) + + +@product_router.post( + "/{product_id}/circularity_properties", + response_model=CircularityPropertiesRead, + status_code=201, + summary="Create product circularity properties", +) +async def create_product_circularity_properties( + product: UserOwnedProductDep, + properties: CircularityPropertiesCreate, + session: AsyncSessionDep, +) -> CircularityProperties: + """Create circularity properties for a product.""" + return await crud.create_circularity_properties(session, properties, product.id) + + +@product_router.patch( + "/{product_id}/circularity_properties", + response_model=CircularityPropertiesRead, + summary="Update product circularity properties", +) +async def update_product_circularity_properties( + product: UserOwnedProductDep, + properties: CircularityPropertiesUpdate, + session: AsyncSessionDep, +) -> CircularityProperties: + """Update circularity properties for a product.""" + return await crud.update_circularity_properties(session, product.id, properties) + + +@product_router.delete( + "/{product_id}/circularity_properties", + status_code=204, + summary="Delete product circularity properties", +) +async def delete_product_circularity_properties( + product: UserOwnedProductDep, + session: AsyncSessionDep, +) -> None: + """Delete circularity properties for a product.""" + await crud.delete_circularity_properties(session, product) + + ## Product Video routers ## @product_router.get( "/{product_id}/videos", diff --git a/backend/app/api/data_collection/schemas.py b/backend/app/api/data_collection/schemas.py index 6306902..b21355d 100644 --- a/backend/app/api/data_collection/schemas.py +++ b/backend/app/api/data_collection/schemas.py @@ -27,6 +27,7 @@ ProductRead, ) from app.api.data_collection.models import ( + CircularityPropertiesBase, PhysicalPropertiesBase, ProductBase, ) @@ -93,6 +94,71 @@ class PhysicalPropertiesUpdate(BaseUpdateSchema, PhysicalPropertiesBase): model_config: ConfigDict = ConfigDict(json_schema_extra={"examples": [{"weight_kg": 15, "height_cm": 120}]}) +class CircularityPropertiesCreate(BaseCreateSchema, CircularityPropertiesBase): + """Schema for creating circularity properties.""" + + model_config: ConfigDict = ConfigDict( + json_schema_extra={ + "examples": [ + { + "recyclability_observation": "The product can be easily disassembled and materials separated", + "recyclability_comment": "High recyclability rating", + "recyclability_reference": "ISO 14021:2016", + "repairability_observation": "Components are modular and can be replaced individually", + "repairability_comment": "Good repairability score", + "repairability_reference": "EN 45554:2020", + "remanufacturability_observation": "Core components can be refurbished and reused", + "remanufacturability_comment": "Suitable for remanufacturing", + "remanufacturability_reference": "BS 8887-2:2009", + } + ] + } + ) + + +class CircularityPropertiesRead(BaseReadSchemaWithTimeStamp, CircularityPropertiesBase): + """Schema for reading circularity properties.""" + + model_config: ConfigDict = ConfigDict( + json_schema_extra={ + "examples": [ + { + "id": 1, + "recyclability_observation": "The product can be easily disassembled and materials separated", + "recyclability_comment": "High recyclability rating", + "recyclability_reference": "ISO 14021:2016", + "repairability_observation": "Components are modular and can be replaced individually", + "repairability_comment": "Good repairability score", + "repairability_reference": "EN 45554:2020", + "remanufacturability_observation": "Core components can be refurbished and reused", + "remanufacturability_comment": "Suitable for remanufacturing", + "remanufacturability_reference": "BS 8887-2:2009", + } + ] + } + ) + + +class CircularityPropertiesUpdate(BaseUpdateSchema, CircularityPropertiesBase): + """Schema for updating circularity properties.""" + + # Make all fields optional for updates + recyclability_observation: str | None = Field(default=None, min_length=2, max_length=500) + repairability_observation: str | None = Field(default=None, min_length=2, max_length=500) + remanufacturability_observation: str | None = Field(default=None, min_length=2, max_length=500) + + model_config: ConfigDict = ConfigDict( + json_schema_extra={ + "examples": [ + { + "recyclability_observation": "Updated observation on recyclability", + "recyclability_comment": "Updated comment", + } + ] + } + ) + + ### Product Schemas ### @@ -128,6 +194,9 @@ class ProductCreateWithRelationships(ProductCreateBase): physical_properties: PhysicalPropertiesCreate | None = Field( default=None, description="Physical properties of the product" ) + circularity_properties: CircularityPropertiesCreate | None = Field( + default=None, description="Circularity properties of the product" + ) videos: list[VideoCreateWithinProduct] = Field(default_factory=list, description="Disassembly videos") bill_of_materials: list[MaterialProductLinkCreateWithinProduct] = Field( @@ -222,6 +291,7 @@ class ProductReadWithProperties(ProductRead): """Schema for reading product information with all properties.""" physical_properties: PhysicalPropertiesRead | None = None + circularity_properties: CircularityPropertiesRead | None = None class ProductReadWithRelationships(ProductReadWithProperties): @@ -290,3 +360,4 @@ class ProductUpdateWithProperties(ProductUpdate): """Schema for a partial update of a product with properties.""" physical_properties: PhysicalPropertiesUpdate | None = None + circularity_properties: CircularityPropertiesUpdate | None = None From d9d2edb2a68086a6312af894e5588d626e142c04 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 17 Nov 2025 13:03:44 +0000 Subject: [PATCH 49/79] fix(backend): Don't initialize email checker for on synthetic user creation --- backend/app/api/auth/services/user_manager.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/app/api/auth/services/user_manager.py b/backend/app/api/auth/services/user_manager.py index c8a312c..f0ab70d 100644 --- a/backend/app/api/auth/services/user_manager.py +++ b/backend/app/api/auth/services/user_manager.py @@ -58,9 +58,15 @@ async def create( request: Request | None = None, ) -> User: """Override of base user creation with additional username uniqueness check and organization creation.""" - try: + # HACK: Skipping of emails for synthetic users is implemented in an ugly way here + # Skip initialization of email checker if sending registration email is disabled + if request and hasattr(request.state, "send_registration_email") and not request.state.send_registration_email: + email_checker = None + else: # Get email checker from app state if request is available - email_checker = request.app.state.email_checker if request else None + email_checker = request.app.state.email_checker if (request and request.app and hasattr(request.app.state, "email_checker")) else None + + try: user_create = await create_user_override(self.user_db, user_create, email_checker) # HACK: This is a temporary solution to allow error propagation for username and organization creation errors. # The built-in UserManager register route can only catch UserAlreadyExists and InvalidPasswordException errors. From 3b495192ab72c4ff1af0228a553a77c25304c7ea Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 17 Nov 2025 14:30:30 +0100 Subject: [PATCH 50/79] feature(backend): Allow superuser to remove any product --- backend/app/api/data_collection/dependencies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/api/data_collection/dependencies.py b/backend/app/api/data_collection/dependencies.py index 666577a..b8bb5b7 100644 --- a/backend/app/api/data_collection/dependencies.py +++ b/backend/app/api/data_collection/dependencies.py @@ -38,7 +38,7 @@ async def get_user_owned_product( current_user: CurrentActiveVerifiedUserDep, ) -> Product: """Verify that the current user owns the specified product.""" - if product.owner_id == current_user.id: + if product.owner_id == current_user.id or current_user.is_superuser: return product raise UserOwnershipError(model_type=Product, model_id=product.id, user_id=current_user.id) from None From e66f2864482c3ee527309cce2c9c960b554b6fd1 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 17 Nov 2025 15:00:07 +0100 Subject: [PATCH 51/79] feature(backend): Move from kg to g for physical_properties weight field --- ...fa19f62_move_from_weight_kg_to_weight_g.py | 55 +++++++++++++++++++ backend/app/api/data_collection/filters.py | 4 +- backend/app/api/data_collection/models.py | 2 +- backend/app/api/data_collection/routers.py | 26 ++++----- backend/app/api/data_collection/schemas.py | 12 ++-- backend/scripts/seed/dummy_data.py | 4 +- docs/docs/architecture/datamodel.md | 2 +- .../product/ProductPhysicalProperties.tsx | 2 +- frontend-app/src/services/api/fetching.ts | 4 +- frontend-app/src/services/api/saving.ts | 4 +- 10 files changed, 85 insertions(+), 30 deletions(-) create mode 100644 backend/alembic/versions/0faa2fa19f62_move_from_weight_kg_to_weight_g.py diff --git a/backend/alembic/versions/0faa2fa19f62_move_from_weight_kg_to_weight_g.py b/backend/alembic/versions/0faa2fa19f62_move_from_weight_kg_to_weight_g.py new file mode 100644 index 0000000..3641e9d --- /dev/null +++ b/backend/alembic/versions/0faa2fa19f62_move_from_weight_kg_to_weight_g.py @@ -0,0 +1,55 @@ +"""Move from weight_kg to weight_g + +Revision ID: 0faa2fa19f62 +Revises: b43d157d07f1 +Create Date: 2025-11-17 14:52:08.201228 + +""" + +from collections.abc import Sequence +from typing import Union + +import sqlalchemy as sa +import sqlmodel + +import app.api.common.models.custom_types +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "0faa2fa19f62" +down_revision: str | None = "b43d157d07f1" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column("physicalproperties", sa.Column("weight_g", sa.Float(), nullable=True)) + + # Migrate data: convert kg to g (multiply by 1000) + op.execute(""" + UPDATE physicalproperties + SET weight_g = weight_kg * 1000 + WHERE weight_kg IS NOT NULL + """) + + op.drop_column("physicalproperties", "weight_kg") + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "physicalproperties", + sa.Column("weight_kg", sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True), + ) + + # Migrate data back: convert g to kg (divide by 1000) + op.execute(""" + UPDATE physicalproperties + SET weight_kg = weight_g / 1000 + WHERE weight_g IS NOT NULL + """) + + op.drop_column("physicalproperties", "weight_g") + # ### end Alembic commands ### diff --git a/backend/app/api/data_collection/filters.py b/backend/app/api/data_collection/filters.py index 4d40d20..47ed408 100644 --- a/backend/app/api/data_collection/filters.py +++ b/backend/app/api/data_collection/filters.py @@ -31,8 +31,8 @@ class Constants(Filter.Constants): class PhysicalPropertiesFilter(Filter): """FastAPI-filter class for Physical Properties filtering.""" - weight_kg__gte: float | None = None - weight_kg__lte: float | None = None + weight_g__gte: float | None = None + weight_g__lte: float | None = None height_cm__gte: float | None = None height_cm__lte: float | None = None width_cm__gte: float | None = None diff --git a/backend/app/api/data_collection/models.py b/backend/app/api/data_collection/models.py index c96f59c..1197b98 100644 --- a/backend/app/api/data_collection/models.py +++ b/backend/app/api/data_collection/models.py @@ -35,7 +35,7 @@ def validate_start_and_end_time(start_time: datetime, end_time: datetime | None) class PhysicalPropertiesBase(CustomBase): """Base model to store physical properties of a product.""" - weight_kg: float | None = Field(default=None, gt=0) + weight_g: float | None = Field(default=None, gt=0) height_cm: float | None = Field(default=None, gt=0) width_cm: float | None = Field(default=None, gt=0) depth_cm: float | None = Field(default=None, gt=0) diff --git a/backend/app/api/data_collection/routers.py b/backend/app/api/data_collection/routers.py index 50cc836..de465ed 100644 --- a/backend/app/api/data_collection/routers.py +++ b/backend/app/api/data_collection/routers.py @@ -389,7 +389,7 @@ async def create_product( "dismantling_time_end": "2025-09-22T16:30:45Z", "product_type_id": 1, "physical_properties": { - "weight_kg": 20, + "weight_g": 2000, "height_cm": 150, "width_cm": 70, "depth_cm": 50, @@ -398,8 +398,8 @@ async def create_product( {"url": "https://www.youtube.com/watch?v=123456789", "description": "Disassembly video"} ], "bill_of_materials": [ - {"quantity": 15, "unit": "kg", "material_id": 1}, - {"quantity": 5, "unit": "kg", "material_id": 2}, + {"quantity": 15, "unit": "g", "material_id": 1}, + {"quantity": 5, "unit": "g", "material_id": 2}, ], }, }, @@ -414,7 +414,7 @@ async def create_product( "dismantling_time_end": "2025-09-22T16:30:45Z", "product_type_id": 1, "physical_properties": { - "weight_kg": 20, + "weight_g": 20000, "height_cm": 150, "width_cm": 70, "depth_cm": 50, @@ -434,7 +434,7 @@ async def create_product( "amount_in_parent": 1, "product_type_id": 2, "physical_properties": { - "weight_kg": 5, + "weight_g": 5000, "height_cm": 50, "width_cm": 40, "depth_cm": 30, @@ -445,15 +445,15 @@ async def create_product( "description": "Seat cushion assembly", "amount_in_parent": 1, "physical_properties": { - "weight_kg": 2, + "weight_g": 2000, "height_cm": 10, "width_cm": 40, "depth_cm": 30, }, "product_type_id": 3, "bill_of_materials": [ - {"quantity": 1.5, "unit": "kg", "material_id": 1}, - {"quantity": 0.5, "unit": "kg", "material_id": 2}, + {"quantity": 1.5, "unit": "g", "material_id": 1}, + {"quantity": 0.5, "unit": "g", "material_id": 2}, ], } ], @@ -668,7 +668,7 @@ async def add_component_to_product( "name": "Seat Assembly", "description": "Chair seat component", "amount_in_parent": 1, - "bill_of_materials": [{"material_id": 1, "quantity": 0.5, "unit": "kg"}], + "bill_of_materials": [{"material_id": 1, "quantity": 0.5, "unit": "g"}], }, }, "nested": { @@ -683,7 +683,7 @@ async def add_component_to_product( "name": "Cushion", "description": "Foam cushion", "amount_in_parent": 1, - "bill_of_materials": [{"material_id": 2, "quantity": 0.3, "unit": "kg"}], + "bill_of_materials": [{"material_id": 2, "quantity": 0.3, "unit": "g"}], } ], }, @@ -1007,8 +1007,8 @@ async def add_materials_to_product( description="List of materials-product links to add to the product", examples=[ [ - {"material_id": 1, "quantity": 5, "unit": "kg"}, - {"material_id": 2, "quantity": 10, "unit": "kg"}, + {"material_id": 1, "quantity": 5, "unit": "g"}, + {"material_id": 2, "quantity": 10, "unit": "g"}, ] ], ), @@ -1035,7 +1035,7 @@ async def add_material_to_product( MaterialProductLinkCreateWithinProductAndMaterial, Body( description="Material-product link details", - examples=[[{"quantity": 5, "unit": "kg"}]], + examples=[[{"quantity": 5, "unit": "g"}]], ), ], session: AsyncSessionDep, diff --git a/backend/app/api/data_collection/schemas.py b/backend/app/api/data_collection/schemas.py index b21355d..32794af 100644 --- a/backend/app/api/data_collection/schemas.py +++ b/backend/app/api/data_collection/schemas.py @@ -76,7 +76,7 @@ class PhysicalPropertiesCreate(BaseCreateSchema, PhysicalPropertiesBase): """Schema for creating physical properties.""" model_config: ConfigDict = ConfigDict( - json_schema_extra={"examples": [{"weight_kg": 20, "height_cm": 150, "width_cm": 70, "depth_cm": 50}]} + json_schema_extra={"examples": [{"weight_g": 20000, "height_cm": 150, "width_cm": 70, "depth_cm": 50}]} ) @@ -84,14 +84,14 @@ class PhysicalPropertiesRead(BaseReadSchemaWithTimeStamp, PhysicalPropertiesBase """Schema for reading physical properties.""" model_config: ConfigDict = ConfigDict( - json_schema_extra={"examples": [{"id": 1, "weight_kg": 20, "height_cm": 150, "width_cm": 70, "depth_cm": 50}]} + json_schema_extra={"examples": [{"id": 1, "weight_g": 20000, "height_cm": 150, "width_cm": 70, "depth_cm": 50}]} ) class PhysicalPropertiesUpdate(BaseUpdateSchema, PhysicalPropertiesBase): """Schema for updating physical properties.""" - model_config: ConfigDict = ConfigDict(json_schema_extra={"examples": [{"weight_kg": 15, "height_cm": 120}]}) + model_config: ConfigDict = ConfigDict(json_schema_extra={"examples": [{"weight_g": 15000, "height_cm": 120}]}) class CircularityPropertiesCreate(BaseCreateSchema, CircularityPropertiesBase): @@ -219,7 +219,7 @@ class ProductCreateBaseProduct(ProductCreateWithRelationships): "dismantling_time_end": "2025-09-22T16:30:45Z", "product_type_id": 1, "physical_properties": { - "weight_kg": 20, + "weight_g": 20000, "height_cm": 150, "width_cm": 70, "depth_cm": 50, @@ -228,8 +228,8 @@ class ProductCreateBaseProduct(ProductCreateWithRelationships): {"url": "https://www.youtube.com/watch?v=123456789", "description": "Disassembly video"} ], "bill_of_materials": [ - {"quantity": 0.3, "unit": "kg", "material_id": 1}, - {"quantity": 0.1, "unit": "kg", "material_id": 2}, + {"quantity": 0.3, "unit": "g", "material_id": 1}, + {"quantity": 0.1, "unit": "g", "material_id": 2}, ], } ] diff --git a/backend/scripts/seed/dummy_data.py b/backend/scripts/seed/dummy_data.py index b084400..e9d4a6e 100755 --- a/backend/scripts/seed/dummy_data.py +++ b/backend/scripts/seed/dummy_data.py @@ -144,7 +144,7 @@ "model": "A2403", "product_type_name": "Smartphone", "physical_properties": { - "weight_kg": 0.164, + "weight_g": 164, "height_cm": 14.7, "width_cm": 7.15, "depth_cm": 0.74, @@ -161,7 +161,7 @@ "model": "XPS9380", "product_type_name": "Laptop", "physical_properties": { - "weight_kg": 1.23, + "weight_g": 1230, "height_cm": 1.16, "width_cm": 30.2, "depth_cm": 19.9, diff --git a/docs/docs/architecture/datamodel.md b/docs/docs/architecture/datamodel.md index 68ac873..315ec0a 100644 --- a/docs/docs/architecture/datamodel.md +++ b/docs/docs/architecture/datamodel.md @@ -207,7 +207,7 @@ erDiagram PHYSICALPROPERTIES { integer id PK - float weight_kg + float weight_g float height_cm float width_cm float depth_cm diff --git a/frontend-app/src/components/product/ProductPhysicalProperties.tsx b/frontend-app/src/components/product/ProductPhysicalProperties.tsx index 9b34019..cb855ce 100644 --- a/frontend-app/src/components/product/ProductPhysicalProperties.tsx +++ b/frontend-app/src/components/product/ProductPhysicalProperties.tsx @@ -13,7 +13,7 @@ interface Props { } const unitMap = { - weight: 'kg', + weight: 'g', height: 'cm', width: 'cm', depth: 'cm', diff --git a/frontend-app/src/services/api/fetching.ts b/frontend-app/src/services/api/fetching.ts index a46662b..9cb423b 100644 --- a/frontend-app/src/services/api/fetching.ts +++ b/frontend-app/src/services/api/fetching.ts @@ -15,7 +15,7 @@ type ProductData = { created_at: string; updated_at: string; product_type_id: number; - physical_properties: { weight_kg: number; height_cm: number; width_cm: number; depth_cm: number }; + physical_properties: { weight_g: number; height_cm: number; width_cm: number; depth_cm: number }; circularity_properties: { recyclability_comment: string | null; recyclability_observation: string; @@ -63,7 +63,7 @@ async function toProduct(data: ProductData): Promise { ownedBy: data.owner_id === meId ? 'me' : data.owner_id, amountInParent: data.amount_in_parent ?? undefined, physicalProperties: { - weight: data.physical_properties.weight_kg, + weight: data.physical_properties.weight_g, height: data.physical_properties.height_cm, width: data.physical_properties.width_cm, depth: data.physical_properties.depth_cm, diff --git a/frontend-app/src/services/api/saving.ts b/frontend-app/src/services/api/saving.ts index 8c9936e..46ac57c 100644 --- a/frontend-app/src/services/api/saving.ts +++ b/frontend-app/src/services/api/saving.ts @@ -18,7 +18,7 @@ function toNewProduct(product: Product): any { bill_of_materials: [ { quantity: 42, - unit: 'kg', + unit: 'g', material_id: 1, }, ], @@ -47,7 +47,7 @@ function toUpdateProduct(product: Product): any { function toUpdatePhysicalProperties(product: Product): any { return { - weight_kg: product.physicalProperties.weight || null, + weight_g: product.physicalProperties.weight || null, height_cm: product.physicalProperties.height || null, width_cm: product.physicalProperties.width || null, depth_cm: product.physicalProperties.depth || null, From 0cd5def5901bd8a940a4b22a263790166ceb8d51 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 17 Nov 2025 15:44:42 +0100 Subject: [PATCH 52/79] feature: paginate users/me/products --- backend/app/api/data_collection/routers.py | 17 ++++++++++++++--- frontend-app/src/services/api/fetching.ts | 3 +-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/backend/app/api/data_collection/routers.py b/backend/app/api/data_collection/routers.py index de465ed..8fdc0f9 100644 --- a/backend/app/api/data_collection/routers.py +++ b/backend/app/api/data_collection/routers.py @@ -106,7 +106,7 @@ async def redirect_to_current_user_products( @user_product_router.get( "", - response_model=list[ProductReadWithRelationshipsAndFlatComponents], + response_model=Page[ProductReadWithRelationshipsAndFlatComponents], summary="Get products collected by a user", ) async def get_user_products( @@ -139,18 +139,29 @@ async def get_user_products( }, ), ] = None, + *, + include_components_as_base_products: Annotated[ + bool | None, + Query(description="Whether to include components as base products in the response"), + ] = None, ) -> Sequence[Product]: """Get products collected by a specific user.""" # NOTE: If needed, we can open up this endpoint to any user by removing this ownership check if user_id != current_user.id and not current_user.is_superuser: raise HTTPException(status_code=403, detail="Not authorized to view this user's products") + + statement=(select(Product).where(Product.owner_id == user_id)) + + if not include_components_as_base_products: + statement: SelectOfScalar[Product] = statement.where(Product.parent_id == None) - return await get_models( + return await get_paginated_models( session, Product, include_relationships=include, model_filter=product_filter, - statement=(select(Product).where(Product.owner_id == user_id)), + statement=statement, + read_schema=ProductReadWithRelationshipsAndFlatComponents, ) diff --git a/frontend-app/src/services/api/fetching.ts b/frontend-app/src/services/api/fetching.ts index 9cb423b..ba932b0 100644 --- a/frontend-app/src/services/api/fetching.ts +++ b/frontend-app/src/services/api/fetching.ts @@ -203,8 +203,7 @@ export async function myProducts( const data = await response.json(); - // TODO: Update to data.items when adding pagination to /users/me/products endpoint - const product_data = data as ProductData[]; + const product_data = data.items as ProductData[]; return Promise.all(product_data.map((data) => toProduct(data))); } From 100039d7ffcb6abc71ec1b107385b79abe16f013 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 24 Nov 2025 10:20:34 +0000 Subject: [PATCH 53/79] fix(backend): make circularity properties optional --- backend/app/api/data_collection/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/app/api/data_collection/models.py b/backend/app/api/data_collection/models.py index 1197b98..c981167 100644 --- a/backend/app/api/data_collection/models.py +++ b/backend/app/api/data_collection/models.py @@ -65,17 +65,17 @@ class CircularityPropertiesBase(CustomBase): """Base model to store circularity properties of a product.""" # Recyclability - recyclability_observation: str = Field(min_length=2, max_length=500) + recyclability_observation: str | None = Field(default=None, min_length=2, max_length=500) recyclability_comment: str | None = Field(default=None, max_length=100) recyclability_reference: str | None = Field(default=None, max_length=100) # Repairability - repairability_observation: str = Field(min_length=2, max_length=500) + repairability_observation: str | None = Field(default=None, min_length=2, max_length=500) repairability_comment: str | None = Field(default=None, max_length=100) repairability_reference: str | None = Field(default=None, max_length=100) # Remanufacturability - remanufacturability_observation: str = Field(min_length=2, max_length=500) + remanufacturability_observation: str | None = Field(default=None, min_length=2, max_length=500) remanufacturability_comment: str | None = Field(default=None, max_length=100) remanufacturability_reference: str | None = Field(default=None, max_length=100) From 71a680e38604a49dc83cae7d3d5f66245b5bacb1 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 25 Nov 2025 10:10:56 +0000 Subject: [PATCH 54/79] chore(backend): Update to python 3.14 --- backend/.python-version | 2 +- backend/Dockerfile | 5 +- backend/Dockerfile.dev | 2 +- backend/Dockerfile.migrations | 3 +- backend/uv.lock | 532 +++++++++++++++++++--------------- codemeta.json | 2 +- 6 files changed, 307 insertions(+), 239 deletions(-) diff --git a/backend/.python-version b/backend/.python-version index 24ee5b1..6324d40 100644 --- a/backend/.python-version +++ b/backend/.python-version @@ -1 +1 @@ -3.13 +3.14 diff --git a/backend/Dockerfile b/backend/Dockerfile index 15ca725..17b19a7 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,6 +1,5 @@ # --- Builder stage --- -# TODO: Move to Python 3.14 once asyncpg supports it (version 0.31.0+, see https://github.com/MagicStack/asyncpg/issues/1282) -FROM ghcr.io/astral-sh/uv:0.9-python3.13-trixie-slim AS builder +FROM ghcr.io/astral-sh/uv:0.9-python3.14-trixie-slim AS builder # Install git for custom dependencies (fastapi-users-db-sqlmodel) RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ @@ -35,7 +34,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --locked --no-editable --no-default-groups --group=api # --- Final runtime stage --- -FROM python:3.13-slim +FROM python:3.14-slim # Build arguments ARG WORKDIR=/opt/relab/backend diff --git a/backend/Dockerfile.dev b/backend/Dockerfile.dev index 5609289..36cea79 100644 --- a/backend/Dockerfile.dev +++ b/backend/Dockerfile.dev @@ -1,6 +1,6 @@ # Development Dockerfile for FastAPI Backend # Note: This requires mounting the source code as a volume in docker-compose.override.yml -FROM ghcr.io/astral-sh/uv:0.9-python3.13-trixie-slim +FROM ghcr.io/astral-sh/uv:0.9-python3.14-trixie-slim # Build arguments ARG WORKDIR=/opt/relab/backend diff --git a/backend/Dockerfile.migrations b/backend/Dockerfile.migrations index 6c03c35..8d32ac9 100644 --- a/backend/Dockerfile.migrations +++ b/backend/Dockerfile.migrations @@ -1,6 +1,5 @@ # --- Builder stage --- -FROM ghcr.io/astral-sh/uv:0.9-python3.13-trixie-slim AS builder - +FROM ghcr.io/astral-sh/uv:0.9-python3.14-trixie-slim AS builder WORKDIR /opt/relab/backend_migrations # Install git for custom dependencies (fastapi-users-db-sqlmodel) diff --git a/backend/uv.lock b/backend/uv.lock index c60bc1c..2f9bde6 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -17,16 +17,16 @@ wheels = [ [[package]] name = "alembic" -version = "1.17.1" +version = "1.17.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mako" }, { name = "sqlalchemy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6e/b6/2a81d7724c0c124edc5ec7a167e85858b6fd31b9611c6fb8ecf617b7e2d3/alembic-1.17.1.tar.gz", hash = "sha256:8a289f6778262df31571d29cca4c7fbacd2f0f582ea0816f4c399b6da7528486", size = 1981285, upload-time = "2025-10-29T00:23:16.667Z" } +sdist = { url = "https://files.pythonhosted.org/packages/02/a6/74c8cadc2882977d80ad756a13857857dbcf9bd405bc80b662eb10651282/alembic-1.17.2.tar.gz", hash = "sha256:bbe9751705c5e0f14877f02d46c53d10885e377e3d90eda810a016f9baa19e8e", size = 1988064, upload-time = "2025-11-14T20:35:04.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/32/7df1d81ec2e50fb661944a35183d87e62d3f6c6d9f8aff64a4f245226d55/alembic-1.17.1-py3-none-any.whl", hash = "sha256:cbc2386e60f89608bb63f30d2d6cc66c7aaed1fe105bd862828600e5ad167023", size = 247848, upload-time = "2025-10-29T00:23:18.79Z" }, + { url = "https://files.pythonhosted.org/packages/ba/88/6237e97e3385b57b5f1528647addea5cc03d4d65d5979ab24327d41fb00d/alembic-1.17.2-py3-none-any.whl", hash = "sha256:f483dd1fe93f6c5d49217055e4d15b905b425b6af906746abb35b69c1996c4e6", size = 248554, upload-time = "2025-11-14T20:35:05.699Z" }, ] [[package]] @@ -57,11 +57,11 @@ wheels = [ [[package]] name = "annotated-doc" -version = "0.0.3" +version = "0.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/a6/dc46877b911e40c00d395771ea710d5e77b6de7bacd5fdcd78d70cc5a48f/annotated_doc-0.0.3.tar.gz", hash = "sha256:e18370014c70187422c33e945053ff4c286f453a984eba84d0dbfa0c935adeda", size = 5535, upload-time = "2025-10-24T14:57:10.718Z" } +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/02/b7/cf592cb5de5cb3bade3357f8d2cf42bf103bbe39f459824b4939fd212911/annotated_doc-0.0.3-py3-none-any.whl", hash = "sha256:348ec6664a76f1fd3be81f43dffbee4c7e8ce931ba71ec67cc7f4ade7fbbb580", size = 5488, upload-time = "2025-10-24T14:57:09.462Z" }, + { 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]] @@ -143,18 +143,34 @@ wheels = [ [[package]] name = "asyncpg" -version = "0.30.0" +version = "0.31.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/4c/7c991e080e106d854809030d8584e15b2e996e26f16aee6d757e387bc17d/asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851", size = 957746, upload-time = "2024-10-20T00:30:41.127Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/22/e20602e1218dc07692acf70d5b902be820168d6282e69ef0d3cb920dc36f/asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70", size = 670373, upload-time = "2024-10-20T00:29:55.165Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b3/0cf269a9d647852a95c06eb00b815d0b95a4eb4b55aa2d6ba680971733b9/asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3", size = 634745, upload-time = "2024-10-20T00:29:57.14Z" }, - { url = "https://files.pythonhosted.org/packages/8e/6d/a4f31bf358ce8491d2a31bfe0d7bcf25269e80481e49de4d8616c4295a34/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33", size = 3512103, upload-time = "2024-10-20T00:29:58.499Z" }, - { url = "https://files.pythonhosted.org/packages/96/19/139227a6e67f407b9c386cb594d9628c6c78c9024f26df87c912fabd4368/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4", size = 3592471, upload-time = "2024-10-20T00:30:00.354Z" }, - { url = "https://files.pythonhosted.org/packages/67/e4/ab3ca38f628f53f0fd28d3ff20edff1c975dd1cb22482e0061916b4b9a74/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4", size = 3496253, upload-time = "2024-10-20T00:30:02.794Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5f/0bf65511d4eeac3a1f41c54034a492515a707c6edbc642174ae79034d3ba/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba", size = 3662720, upload-time = "2024-10-20T00:30:04.501Z" }, - { url = "https://files.pythonhosted.org/packages/e7/31/1513d5a6412b98052c3ed9158d783b1e09d0910f51fbe0e05f56cc370bc4/asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590", size = 560404, upload-time = "2024-10-20T00:30:06.537Z" }, - { url = "https://files.pythonhosted.org/packages/c8/a4/cec76b3389c4c5ff66301cd100fe88c318563ec8a520e0b2e792b5b84972/asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e", size = 621623, upload-time = "2024-10-20T00:30:09.024Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/fe/cc/d18065ce2380d80b1bcce927c24a2642efd38918e33fd724bc4bca904877/asyncpg-0.31.0.tar.gz", hash = "sha256:c989386c83940bfbd787180f2b1519415e2d3d6277a70d9d0f0145ac73500735", size = 993667, upload-time = "2025-11-24T23:27:00.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/11/97b5c2af72a5d0b9bc3fa30cd4b9ce22284a9a943a150fdc768763caf035/asyncpg-0.31.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c204fab1b91e08b0f47e90a75d1b3c62174dab21f670ad6c5d0f243a228f015b", size = 661111, upload-time = "2025-11-24T23:26:04.467Z" }, + { url = "https://files.pythonhosted.org/packages/1b/71/157d611c791a5e2d0423f09f027bd499935f0906e0c2a416ce712ba51ef3/asyncpg-0.31.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:54a64f91839ba59008eccf7aad2e93d6e3de688d796f35803235ea1c4898ae1e", size = 636928, upload-time = "2025-11-24T23:26:05.944Z" }, + { url = "https://files.pythonhosted.org/packages/2e/fc/9e3486fb2bbe69d4a867c0b76d68542650a7ff1574ca40e84c3111bb0c6e/asyncpg-0.31.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0e0822b1038dc7253b337b0f3f676cadc4ac31b126c5d42691c39691962e403", size = 3424067, upload-time = "2025-11-24T23:26:07.957Z" }, + { url = "https://files.pythonhosted.org/packages/12/c6/8c9d076f73f07f995013c791e018a1cd5f31823c2a3187fc8581706aa00f/asyncpg-0.31.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bef056aa502ee34204c161c72ca1f3c274917596877f825968368b2c33f585f4", size = 3518156, upload-time = "2025-11-24T23:26:09.591Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3b/60683a0baf50fbc546499cfb53132cb6835b92b529a05f6a81471ab60d0c/asyncpg-0.31.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0bfbcc5b7ffcd9b75ab1558f00db2ae07db9c80637ad1b2469c43df79d7a5ae2", size = 3319636, upload-time = "2025-11-24T23:26:11.168Z" }, + { url = "https://files.pythonhosted.org/packages/50/dc/8487df0f69bd398a61e1792b3cba0e47477f214eff085ba0efa7eac9ce87/asyncpg-0.31.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:22bc525ebbdc24d1261ecbf6f504998244d4e3be1721784b5f64664d61fbe602", size = 3472079, upload-time = "2025-11-24T23:26:13.164Z" }, + { url = "https://files.pythonhosted.org/packages/13/a1/c5bbeeb8531c05c89135cb8b28575ac2fac618bcb60119ee9696c3faf71c/asyncpg-0.31.0-cp313-cp313-win32.whl", hash = "sha256:f890de5e1e4f7e14023619399a471ce4b71f5418cd67a51853b9910fdfa73696", size = 527606, upload-time = "2025-11-24T23:26:14.78Z" }, + { url = "https://files.pythonhosted.org/packages/91/66/b25ccb84a246b470eb943b0107c07edcae51804912b824054b3413995a10/asyncpg-0.31.0-cp313-cp313-win_amd64.whl", hash = "sha256:dc5f2fa9916f292e5c5c8b2ac2813763bcd7f58e130055b4ad8a0531314201ab", size = 596569, upload-time = "2025-11-24T23:26:16.189Z" }, + { url = "https://files.pythonhosted.org/packages/3c/36/e9450d62e84a13aea6580c83a47a437f26c7ca6fa0f0fd40b6670793ea30/asyncpg-0.31.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f6b56b91bb0ffc328c4e3ed113136cddd9deefdf5f79ab448598b9772831df44", size = 660867, upload-time = "2025-11-24T23:26:17.631Z" }, + { url = "https://files.pythonhosted.org/packages/82/4b/1d0a2b33b3102d210439338e1beea616a6122267c0df459ff0265cd5807a/asyncpg-0.31.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:334dec28cf20d7f5bb9e45b39546ddf247f8042a690bff9b9573d00086e69cb5", size = 638349, upload-time = "2025-11-24T23:26:19.689Z" }, + { url = "https://files.pythonhosted.org/packages/41/aa/e7f7ac9a7974f08eff9183e392b2d62516f90412686532d27e196c0f0eeb/asyncpg-0.31.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98cc158c53f46de7bb677fd20c417e264fc02b36d901cc2a43bd6cb0dc6dbfd2", size = 3410428, upload-time = "2025-11-24T23:26:21.275Z" }, + { url = "https://files.pythonhosted.org/packages/6f/de/bf1b60de3dede5c2731e6788617a512bc0ebd9693eac297ee74086f101d7/asyncpg-0.31.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9322b563e2661a52e3cdbc93eed3be7748b289f792e0011cb2720d278b366ce2", size = 3471678, upload-time = "2025-11-24T23:26:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/46/78/fc3ade003e22d8bd53aaf8f75f4be48f0b460fa73738f0391b9c856a9147/asyncpg-0.31.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19857a358fc811d82227449b7ca40afb46e75b33eb8897240c3839dd8b744218", size = 3313505, upload-time = "2025-11-24T23:26:25.235Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e9/73eb8a6789e927816f4705291be21f2225687bfa97321e40cd23055e903a/asyncpg-0.31.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ba5f8886e850882ff2c2ace5732300e99193823e8107e2c53ef01c1ebfa1e85d", size = 3434744, upload-time = "2025-11-24T23:26:26.944Z" }, + { url = "https://files.pythonhosted.org/packages/08/4b/f10b880534413c65c5b5862f79b8e81553a8f364e5238832ad4c0af71b7f/asyncpg-0.31.0-cp314-cp314-win32.whl", hash = "sha256:cea3a0b2a14f95834cee29432e4ddc399b95700eb1d51bbc5bfee8f31fa07b2b", size = 532251, upload-time = "2025-11-24T23:26:28.404Z" }, + { url = "https://files.pythonhosted.org/packages/d3/2d/7aa40750b7a19efa5d66e67fc06008ca0f27ba1bd082e457ad82f59aba49/asyncpg-0.31.0-cp314-cp314-win_amd64.whl", hash = "sha256:04d19392716af6b029411a0264d92093b6e5e8285ae97a39957b9a9c14ea72be", size = 604901, upload-time = "2025-11-24T23:26:30.34Z" }, + { url = "https://files.pythonhosted.org/packages/ce/fe/b9dfe349b83b9dee28cc42360d2c86b2cdce4cb551a2c2d27e156bcac84d/asyncpg-0.31.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bdb957706da132e982cc6856bb2f7b740603472b54c3ebc77fe60ea3e57e1bd2", size = 702280, upload-time = "2025-11-24T23:26:32Z" }, + { url = "https://files.pythonhosted.org/packages/6a/81/e6be6e37e560bd91e6c23ea8a6138a04fd057b08cf63d3c5055c98e81c1d/asyncpg-0.31.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6d11b198111a72f47154fa03b85799f9be63701e068b43f84ac25da0bda9cb31", size = 682931, upload-time = "2025-11-24T23:26:33.572Z" }, + { url = "https://files.pythonhosted.org/packages/a6/45/6009040da85a1648dd5bc75b3b0a062081c483e75a1a29041ae63a0bf0dc/asyncpg-0.31.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18c83b03bc0d1b23e6230f5bf8d4f217dc9bc08644ce0502a9d91dc9e634a9c7", size = 3581608, upload-time = "2025-11-24T23:26:35.638Z" }, + { url = "https://files.pythonhosted.org/packages/7e/06/2e3d4d7608b0b2b3adbee0d0bd6a2d29ca0fc4d8a78f8277df04e2d1fd7b/asyncpg-0.31.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e009abc333464ff18b8f6fd146addffd9aaf63e79aa3bb40ab7a4c332d0c5e9e", size = 3498738, upload-time = "2025-11-24T23:26:37.275Z" }, + { url = "https://files.pythonhosted.org/packages/7d/aa/7d75ede780033141c51d83577ea23236ba7d3a23593929b32b49db8ed36e/asyncpg-0.31.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3b1fbcb0e396a5ca435a8826a87e5c2c2cc0c8c68eb6fadf82168056b0e53a8c", size = 3401026, upload-time = "2025-11-24T23:26:39.423Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7a/15e37d45e7f7c94facc1e9148c0e455e8f33c08f0b8a0b1deb2c5171771b/asyncpg-0.31.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8df714dba348efcc162d2adf02d213e5fab1bd9f557e1305633e851a61814a7a", size = 3429426, upload-time = "2025-11-24T23:26:41.032Z" }, + { url = "https://files.pythonhosted.org/packages/13/d5/71437c5f6ae5f307828710efbe62163974e71237d5d46ebd2869ea052d10/asyncpg-0.31.0-cp314-cp314t-win32.whl", hash = "sha256:1b41f1afb1033f2b44f3234993b15096ddc9cd71b21a42dbd87fc6a57b43d65d", size = 614495, upload-time = "2025-11-24T23:26:42.659Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d7/8fb3044eaef08a310acfe23dae9a8e2e07d305edc29a53497e52bc76eca7/asyncpg-0.31.0-cp314-cp314t-win_amd64.whl", hash = "sha256:bd4107bb7cdd0e9e65fae66a62afd3a249663b844fa34d479f6d5b3bef9c04c3", size = 706062, upload-time = "2025-11-24T23:26:44.086Z" }, ] [[package]] @@ -231,30 +247,30 @@ wheels = [ [[package]] name = "boto3" -version = "1.40.69" +version = "1.41.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/36/65d292d14261aedbb9a22e5bf194d84c119c889135b42448db646d06d76b/boto3-1.40.69.tar.gz", hash = "sha256:5273f6bac347331a87db809dff97d8736c50c3be19f2bb36ad08c5131c408976", size = 111628, upload-time = "2025-11-07T20:26:26.949Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/30/1f1bfb34a97709b5d004d5c16ccac81a73ea6c5ce86ce75eaff0a75aee3f/boto3-1.41.3.tar.gz", hash = "sha256:8a89f3900a356879022c1600f72cbb3d8b85708f094d2d08a461bd193d0b07ca", size = 111614, upload-time = "2025-11-24T20:22:33.007Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/2f/65009a8d274cd9c7211807c1a07cce17203ffe76368e3ebc4ca03a7b79de/boto3-1.40.69-py3-none-any.whl", hash = "sha256:c3f710a1990c4be1c0db43b938743d4e404c7f1f06d5f1fa0c8e9b1cea4290b2", size = 139361, upload-time = "2025-11-07T20:26:24.522Z" }, + { url = "https://files.pythonhosted.org/packages/37/d3/56e8c147e369fdc1b5526584f87151ca1742949bf5e6ab7500d926107624/boto3-1.41.3-py3-none-any.whl", hash = "sha256:10a3f5a72e071c362f5aa8443bd949edc31b7494c48a315ccdab14b1c387a1fd", size = 139345, upload-time = "2025-11-24T20:22:30.601Z" }, ] [[package]] name = "botocore" -version = "1.40.69" +version = "1.41.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e2/73/42499b183ca5cef25c35338ad2636368b0ae2193654642756492e96ee906/botocore-1.40.69.tar.gz", hash = "sha256:df310ddc4d2de5543ba3df4e4b5f9907a2951896d63a9fbae115c26ca0976951", size = 14440352, upload-time = "2025-11-07T20:26:14.276Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/e9/d6207e08f35280cb8755b316f0e0a0cd2e8405d1b849e847c26fb4e3e3a6/botocore-1.41.3.tar.gz", hash = "sha256:1c6ad338f445c9bf02e231bfa302239d60520ec6dd88ded3206b34dca100103c", size = 14658770, upload-time = "2025-11-24T20:22:21.929Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/d6/bf2b91d4a92af6ee70e0689913414463a48cf51c0fc855c98b94bde8e7f3/botocore-1.40.69-py3-none-any.whl", hash = "sha256:5d810efeb9e18f91f32690642fa81ae60e482eefeea0d35ec72da2e3d924c1a5", size = 14103454, upload-time = "2025-11-07T20:26:09.486Z" }, + { url = "https://files.pythonhosted.org/packages/9f/18/a0597e4491d3a725768162c48a4dd1e1a57323fdb40fca04a34e9a68ef93/botocore-1.41.3-py3-none-any.whl", hash = "sha256:fe2379b30cc726e9e44bf47c3834fe208b85f7eaa57b934ab05f305ca9d05a8b", size = 14328009, upload-time = "2025-11-24T20:22:17.618Z" }, ] [[package]] @@ -268,11 +284,11 @@ wheels = [ [[package]] name = "certifi" -version = "2025.10.5" +version = "2025.11.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519, upload-time = "2025-10-05T04:12:15.808Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286, upload-time = "2025-10-05T04:12:14.03Z" }, + { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" }, ] [[package]] @@ -363,14 +379,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.0" +version = "8.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, ] [[package]] @@ -396,63 +412,63 @@ wheels = [ [[package]] name = "coverage" -version = "7.11.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/89/12/3e2d2ec71796e0913178478e693a06af6a3bc9f7f9cb899bf85a426d8370/coverage-7.11.1.tar.gz", hash = "sha256:b4b3a072559578129a9e863082a2972a2abd8975bc0e2ec57da96afcd6580a8a", size = 814037, upload-time = "2025-11-07T10:52:41.067Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/01/0c50c318f5e8f1a482da05d788d0ff06137803ed8fface4a1ba51e04b3ad/coverage-7.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:da9930594ca99d66eb6f613d7beba850db2f8dfa86810ee35ae24e4d5f2bb97d", size = 216920, upload-time = "2025-11-07T10:50:55.992Z" }, - { url = "https://files.pythonhosted.org/packages/20/11/9f038e6c2baea968c377ab355b0d1d0a46b5f38985691bf51164e1b78c1f/coverage-7.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc47a280dc014220b0fc6e5f55082a3f51854faf08fd9635b8a4f341c46c77d3", size = 217301, upload-time = "2025-11-07T10:50:57.609Z" }, - { url = "https://files.pythonhosted.org/packages/68/cd/9dcf93d81d0cddaa0bba90c3b4580e6f1ddf833918b816930d250cc553a4/coverage-7.11.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:74003324321bbf130939146886eddf92e48e616b5910215e79dea6edeb8ee7c8", size = 248277, upload-time = "2025-11-07T10:50:59.442Z" }, - { url = "https://files.pythonhosted.org/packages/11/f5/b2c7c494046c9c783d3cac4c812fc24d6104dd36a7a598e7dd6fea3e7927/coverage-7.11.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:211f7996265daab60a8249af4ca6641b3080769cbedcffc42cc4841118f3a305", size = 250871, upload-time = "2025-11-07T10:51:01.094Z" }, - { url = "https://files.pythonhosted.org/packages/a5/5a/b359649566954498aa17d7c98093182576d9e435ceb4ea917b3b48d56f86/coverage-7.11.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70619d194d8fea0cb028cb6bb9c85b519c7509c1d1feef1eea635183bc8ecd27", size = 252115, upload-time = "2025-11-07T10:51:03.087Z" }, - { url = "https://files.pythonhosted.org/packages/f3/17/3cef1ede3739622950f0737605353b797ec564e70c9d254521b10f4b03ba/coverage-7.11.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e0208bb59d441cfa3321569040f8e455f9261256e0df776c5462a1e5a9b31e13", size = 248442, upload-time = "2025-11-07T10:51:04.888Z" }, - { url = "https://files.pythonhosted.org/packages/5f/63/d5854c47ae42d9d18855329db6bc528f5b7f4f874257edb00cf8b483f9f8/coverage-7.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:545714d8765bda1c51f8b1c96e0b497886a054471c68211e76ef49dd1468587d", size = 250253, upload-time = "2025-11-07T10:51:06.515Z" }, - { url = "https://files.pythonhosted.org/packages/48/e8/c7706f8a5358a59c18b489e7e19e83d6161b7c8bc60771f95920570c94a8/coverage-7.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d0a2b02c1e20158dd405054bcca87f91fd5b7605626aee87150819ea616edd67", size = 248217, upload-time = "2025-11-07T10:51:08.405Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c9/a2136dfb168eb09e2f6d9d6b6c986243fdc0b3866a9376adb263d3c3378b/coverage-7.11.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0f4aa986a4308a458e0fb572faa3eb3db2ea7ce294604064b25ab32b435a468", size = 248040, upload-time = "2025-11-07T10:51:10.626Z" }, - { url = "https://files.pythonhosted.org/packages/18/9a/a63991c0608ddc6adf65e6f43124951aaf36bd79f41937b028120b8268ea/coverage-7.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d51cc6687e8bbfd1e041f52baed0f979cd592242cf50bf18399a7e03afc82d88", size = 249801, upload-time = "2025-11-07T10:51:12.63Z" }, - { url = "https://files.pythonhosted.org/packages/84/19/947acf7c0c6e90e4ec3abf474133ed36d94407d07e36eafdfd3acb59fee9/coverage-7.11.1-cp313-cp313-win32.whl", hash = "sha256:1b3067db3afe6deeca2b2c9f0ec23820d5f1bd152827acfadf24de145dfc5f66", size = 219430, upload-time = "2025-11-07T10:51:14.329Z" }, - { url = "https://files.pythonhosted.org/packages/35/54/36fef7afb3884450c7b6d494fcabe2fab7c669d547c800ca30f41c1dc212/coverage-7.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:39a4c44b0cd40e3c9d89b2b7303ebd6ab9ae8a63f9e9a8c4d65a181a0b33aebe", size = 220239, upload-time = "2025-11-07T10:51:16.418Z" }, - { url = "https://files.pythonhosted.org/packages/d3/dc/7d38bb99e8e69200b7dd5de15507226bd90eac102dfc7cc891b9934cdc76/coverage-7.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:a2e3560bf82fa8169a577e054cbbc29888699526063fee26ea59ea2627fd6e73", size = 218868, upload-time = "2025-11-07T10:51:18.186Z" }, - { url = "https://files.pythonhosted.org/packages/36/c6/d1ff54fbd6bcad42dbcfd13b417e636ef84aae194353b1ef3361700f2525/coverage-7.11.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47a4f362a10285897ab3aa7a4b37d28213a4f2626823923613d6d7a3584dd79a", size = 217615, upload-time = "2025-11-07T10:51:21.065Z" }, - { url = "https://files.pythonhosted.org/packages/73/f9/6ed59e7cf1488d6f975e5b14ef836f5e537913523e92175135f8518a83ce/coverage-7.11.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0df35fa7419ef571db9dacd50b0517bc54dbfe37eb94043b5fc3540bff276acd", size = 217960, upload-time = "2025-11-07T10:51:22.797Z" }, - { url = "https://files.pythonhosted.org/packages/c4/74/2dab1dc2ebe16f074f80ae483b0f45faf278d102be703ac01b32cd85b6c3/coverage-7.11.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e1a2c621d341c9d56f7917e56fbb56be4f73fe0d0e8dae28352fb095060fd467", size = 259262, upload-time = "2025-11-07T10:51:24.467Z" }, - { url = "https://files.pythonhosted.org/packages/15/49/eccfe039663e29a50a54b0c2c8d076acd174d7ac50d018ef8a5b1c37c8dc/coverage-7.11.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c354b111be9b2234d9573d75dd30ca4e414b7659c730e477e89be4f620b3fb5", size = 261326, upload-time = "2025-11-07T10:51:26.232Z" }, - { url = "https://files.pythonhosted.org/packages/f0/bb/2b829aa23fd5ee8318e33cc02a606eb09900921291497963adc3f06af8bb/coverage-7.11.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4589bd44698728f600233fb2881014c9b8ec86637ef454c00939e779661dbe7e", size = 263758, upload-time = "2025-11-07T10:51:27.912Z" }, - { url = "https://files.pythonhosted.org/packages/ac/03/d44c3d70e5da275caf2cad2071da6b425412fbcb1d1d5a81f1f89b45e3f1/coverage-7.11.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c6956fc8754f2309131230272a7213a483a32ecbe29e2b9316d808a28f2f8ea1", size = 258444, upload-time = "2025-11-07T10:51:30.107Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c1/cf61d9f46ae088774c65dd3387a15dfbc72de90c1f6e105025e9eda19b42/coverage-7.11.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63926a97ed89dc6a087369b92dcb8b9a94cead46c08b33a7f1f4818cd8b6a3c3", size = 261335, upload-time = "2025-11-07T10:51:31.814Z" }, - { url = "https://files.pythonhosted.org/packages/95/9a/b3299bb14f11f2364d78a2b9704491b15395e757af6116694731ce4e5834/coverage-7.11.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f5311ba00c53a7fb2b293fdc1f478b7286fe2a845a7ba9cda053f6e98178f0b4", size = 258951, upload-time = "2025-11-07T10:51:33.925Z" }, - { url = "https://files.pythonhosted.org/packages/3f/a3/73cb2763e59f14ba6d8d6444b1f640a9be2242bfb59b7e50581c695db7ff/coverage-7.11.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:31bf5ffad84c974f9e72ac53493350f36b6fa396109159ec704210698f12860b", size = 257840, upload-time = "2025-11-07T10:51:36.092Z" }, - { url = "https://files.pythonhosted.org/packages/85/db/482e72589a952027e238ffa3a15f192c552e0685fd0c5220ad05b5f17d56/coverage-7.11.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:227ee59fbc4a8c57a7383a1d7af6ca94a78ae3beee4045f38684548a8479a65b", size = 260040, upload-time = "2025-11-07T10:51:38.277Z" }, - { url = "https://files.pythonhosted.org/packages/18/a1/b931d3ee099c2dca8e9ea56c07ae84c0f91562f7bbbcccab8c91b3474ef1/coverage-7.11.1-cp313-cp313t-win32.whl", hash = "sha256:a447d97b3ce680bb1da2e6bd822ebb71be6a1fb77ce2c2ad2fe4bd8aacec3058", size = 220102, upload-time = "2025-11-07T10:51:40.017Z" }, - { url = "https://files.pythonhosted.org/packages/9a/53/b553b7bfa6207def4918f0cb72884c844fa4c3f1566e58fbb4f34e54cdc5/coverage-7.11.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d6d11180437c67bde2248563a42b8e5bbf85c8df78fae13bf818ad17bfb15f02", size = 221166, upload-time = "2025-11-07T10:51:41.921Z" }, - { url = "https://files.pythonhosted.org/packages/6b/45/1c1d58b3ed585598764bd2fe41fcf60ccafe15973ad621c322ba52e22d32/coverage-7.11.1-cp313-cp313t-win_arm64.whl", hash = "sha256:1e19a4c43d612760c6f7190411fb157e2d8a6dde00c91b941d43203bd3b17f6f", size = 219439, upload-time = "2025-11-07T10:51:43.753Z" }, - { url = "https://files.pythonhosted.org/packages/d9/c2/ac2c3417eaa4de1361036ebbc7da664242b274b2e00c4b4a1cfc7b29920b/coverage-7.11.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0305463c45c5f21f0396cd5028de92b1f1387e2e0756a85dd3147daa49f7a674", size = 216967, upload-time = "2025-11-07T10:51:45.55Z" }, - { url = "https://files.pythonhosted.org/packages/5e/a3/afef455d03c468ee303f9df9a6f407e8bea64cd576fca914ff888faf52ca/coverage-7.11.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fa4d468d5efa1eb6e3062be8bd5f45cbf28257a37b71b969a8c1da2652dfec77", size = 217298, upload-time = "2025-11-07T10:51:47.31Z" }, - { url = "https://files.pythonhosted.org/packages/9d/59/6e2fb3fb58637001132dc32228b4fb5b332d75d12f1353cb00fe084ee0ba/coverage-7.11.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d2b2f5fc8fe383cbf2d5c77d6c4b2632ede553bc0afd0cdc910fa5390046c290", size = 248337, upload-time = "2025-11-07T10:51:49.48Z" }, - { url = "https://files.pythonhosted.org/packages/1d/5e/ce442bab963e3388658da8bde6ddbd0a15beda230afafaa25e3c487dc391/coverage-7.11.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bde6488c1ad509f4fb1a4f9960fd003d5a94adef61e226246f9699befbab3276", size = 250853, upload-time = "2025-11-07T10:51:51.215Z" }, - { url = "https://files.pythonhosted.org/packages/d1/2f/43f94557924ca9b64e09f1c3876da4eec44a05a41e27b8a639d899716c0e/coverage-7.11.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a69e0d6fa0b920fe6706a898c52955ec5bcfa7e45868215159f45fd87ea6da7c", size = 252190, upload-time = "2025-11-07T10:51:53.262Z" }, - { url = "https://files.pythonhosted.org/packages/8c/fa/a04e769b92bc5628d4bd909dcc3c8219efe5e49f462e29adc43e198ecfde/coverage-7.11.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:976e51e4a549b80e4639eda3a53e95013a14ff6ad69bb58ed604d34deb0e774c", size = 248335, upload-time = "2025-11-07T10:51:55.388Z" }, - { url = "https://files.pythonhosted.org/packages/99/d0/b98ab5d2abe425c71117a7c690ead697a0b32b83256bf0f566c726b7f77b/coverage-7.11.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d61fcc4d384c82971a3d9cf00d0872881f9ded19404c714d6079b7a4547e2955", size = 250209, upload-time = "2025-11-07T10:51:57.263Z" }, - { url = "https://files.pythonhosted.org/packages/9c/3f/b9c4fbd2e6d1b64098f99fb68df7f7c1b3e0a0968d24025adb24f359cdec/coverage-7.11.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:284c5df762b533fae3ebd764e3b81c20c1c9648d93ef34469759cb4e3dfe13d0", size = 248163, upload-time = "2025-11-07T10:51:59.014Z" }, - { url = "https://files.pythonhosted.org/packages/08/fc/3e4d54fb6368b0628019eefd897fc271badbd025410fd5421a65fb58758f/coverage-7.11.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:bab32cb1d4ad2ac6dcc4e17eee5fa136c2a1d14ae914e4bce6c8b78273aece3c", size = 247983, upload-time = "2025-11-07T10:52:01.027Z" }, - { url = "https://files.pythonhosted.org/packages/b9/4a/a5700764a12e932b35afdddb2f59adbca289c1689455d06437f609f3ef35/coverage-7.11.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:36f2fed9ce392ca450fb4e283900d0b41f05c8c5db674d200f471498be3ce747", size = 249646, upload-time = "2025-11-07T10:52:02.856Z" }, - { url = "https://files.pythonhosted.org/packages/0e/2c/45ed33d9e80a1cc9b44b4bd535d44c154d3204671c65abd90ec1e99522a2/coverage-7.11.1-cp314-cp314-win32.whl", hash = "sha256:853136cecb92a5ba1cc8f61ec6ffa62ca3c88b4b386a6c835f8b833924f9a8c5", size = 219700, upload-time = "2025-11-07T10:52:05.05Z" }, - { url = "https://files.pythonhosted.org/packages/90/d7/5845597360f6434af1290118ebe114642865f45ce47e7e822d9c07b371be/coverage-7.11.1-cp314-cp314-win_amd64.whl", hash = "sha256:77443d39143e20927259a61da0c95d55ffc31cf43086b8f0f11a92da5260d592", size = 220516, upload-time = "2025-11-07T10:52:07.259Z" }, - { url = "https://files.pythonhosted.org/packages/ae/d0/d311a06f9cf7a48a98ffcfd0c57db0dcab6da46e75c439286a50dc648161/coverage-7.11.1-cp314-cp314-win_arm64.whl", hash = "sha256:829acb88fa47591a64bf5197e96a931ce9d4b3634c7f81a224ba3319623cdf6c", size = 219091, upload-time = "2025-11-07T10:52:09.216Z" }, - { url = "https://files.pythonhosted.org/packages/a7/3d/c6a84da4fa9b840933045b19dd19d17b892f3f2dd1612903260291416dba/coverage-7.11.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2ad1fe321d9522ea14399de83e75a11fb6a8887930c3679feb383301c28070d9", size = 217700, upload-time = "2025-11-07T10:52:11.348Z" }, - { url = "https://files.pythonhosted.org/packages/94/10/a4fc5022017dd7ac682dc423849c241dfbdad31734b8f96060d84e70b587/coverage-7.11.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f69c332f0c3d1357c74decc9b1843fcd428cf9221bf196a20ad22aa1db3e1b6c", size = 217968, upload-time = "2025-11-07T10:52:13.203Z" }, - { url = "https://files.pythonhosted.org/packages/59/2d/a554cd98924d296de5816413280ac3b09e42a05fb248d66f8d474d321938/coverage-7.11.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:576baeea4eebde684bf6c91c01e97171c8015765c8b2cfd4022a42b899897811", size = 259334, upload-time = "2025-11-07T10:52:15.079Z" }, - { url = "https://files.pythonhosted.org/packages/05/98/d484cb659ec33958ca96b6f03438f56edc23b239d1ad0417b7a97fc1848a/coverage-7.11.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:28ad84c694fa86084cfd3c1eab4149844b8cb95bd8e5cbfc4a647f3ee2cce2b3", size = 261445, upload-time = "2025-11-07T10:52:17.134Z" }, - { url = "https://files.pythonhosted.org/packages/f3/fa/920cba122cc28f4557c0507f8bd7c6e527ebcc537d0309186f66464a8fd9/coverage-7.11.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b1043ff958f09fc3f552c014d599f3c6b7088ba97d7bc1bd1cce8603cd75b520", size = 263858, upload-time = "2025-11-07T10:52:19.836Z" }, - { url = "https://files.pythonhosted.org/packages/2a/a0/036397bdbee0f3bd46c2e26fdfbb1a61b2140bf9059240c37b61149047fa/coverage-7.11.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c6681add5060c2742dafcf29826dff1ff8eef889a3b03390daeed84361c428bd", size = 258381, upload-time = "2025-11-07T10:52:21.687Z" }, - { url = "https://files.pythonhosted.org/packages/b6/61/2533926eb8990f182eb287f4873216c8ca530cc47241144aabf46fe80abe/coverage-7.11.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:773419b225ec9a75caa1e941dd0c83a91b92c2b525269e44e6ee3e4c630607db", size = 261321, upload-time = "2025-11-07T10:52:23.612Z" }, - { url = "https://files.pythonhosted.org/packages/32/6e/618f7e203a998e4f6b8a0fa395744a416ad2adbcdc3735bc19466456718a/coverage-7.11.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a9cb272a0e0157dbb9b2fd0b201b759bd378a1a6138a16536c025c2ce4f7643b", size = 258933, upload-time = "2025-11-07T10:52:25.514Z" }, - { url = "https://files.pythonhosted.org/packages/22/40/6b1c27f772cb08a14a338647ead1254a57ee9dabbb4cacbc15df7f278741/coverage-7.11.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:e09adb2a7811dc75998eef68f47599cf699e2b62eed09c9fefaeb290b3920f34", size = 257756, upload-time = "2025-11-07T10:52:27.845Z" }, - { url = "https://files.pythonhosted.org/packages/73/07/f9cd12f71307a785ea15b009c8d8cc2543e4a867bd04b8673843970b6b43/coverage-7.11.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1335fa8c2a2fea49924d97e1e3500cfe8d7c849f5369f26bb7559ad4259ccfab", size = 260086, upload-time = "2025-11-07T10:52:29.776Z" }, - { url = "https://files.pythonhosted.org/packages/34/02/31c5394f6f5d72a466966bcfdb61ce5a19862d452816d6ffcbb44add16ee/coverage-7.11.1-cp314-cp314t-win32.whl", hash = "sha256:4782d71d2a4fa7cef95e853b7097c8bbead4dbd0e6f9c7152a6b11a194b794db", size = 220483, upload-time = "2025-11-07T10:52:31.752Z" }, - { url = "https://files.pythonhosted.org/packages/7f/96/81e1ef5fbfd5090113a96e823dbe055e4c58d96ca73b1fb0ad9d26f9ec36/coverage-7.11.1-cp314-cp314t-win_amd64.whl", hash = "sha256:939f45e66eceb63c75e8eb8fc58bb7077c00f1a41b0e15c6ef02334a933cfe93", size = 221592, upload-time = "2025-11-07T10:52:33.724Z" }, - { url = "https://files.pythonhosted.org/packages/38/7a/a5d050de44951ac453a2046a0f3fb5471a4a557f0c914d00db27d543d94c/coverage-7.11.1-cp314-cp314t-win_arm64.whl", hash = "sha256:01c575bdbef35e3f023b50a146e9a75c53816e4f2569109458155cd2315f87d9", size = 219627, upload-time = "2025-11-07T10:52:36.285Z" }, - { url = "https://files.pythonhosted.org/packages/76/32/bd9f48c28e23b2f08946f8e83983617b00619f5538dbd7e1045fa7e88c00/coverage-7.11.1-py3-none-any.whl", hash = "sha256:0fa848acb5f1da24765cee840e1afe9232ac98a8f9431c6112c15b34e880b9e8", size = 208689, upload-time = "2025-11-07T10:52:38.646Z" }, +version = "7.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/89/26/4a96807b193b011588099c3b5c89fbb05294e5b90e71018e065465f34eb6/coverage-7.12.0.tar.gz", hash = "sha256:fc11e0a4e372cb5f282f16ef90d4a585034050ccda536451901abfb19a57f40c", size = 819341, upload-time = "2025-11-18T13:34:20.766Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/14/771700b4048774e48d2c54ed0c674273702713c9ee7acdfede40c2666747/coverage-7.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47324fffca8d8eae7e185b5bb20c14645f23350f870c1649003618ea91a78941", size = 217725, upload-time = "2025-11-18T13:32:49.22Z" }, + { url = "https://files.pythonhosted.org/packages/17/a7/3aa4144d3bcb719bf67b22d2d51c2d577bf801498c13cb08f64173e80497/coverage-7.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ccf3b2ede91decd2fb53ec73c1f949c3e034129d1e0b07798ff1d02ea0c8fa4a", size = 218098, upload-time = "2025-11-18T13:32:50.78Z" }, + { url = "https://files.pythonhosted.org/packages/fc/9c/b846bbc774ff81091a12a10203e70562c91ae71badda00c5ae5b613527b1/coverage-7.12.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b365adc70a6936c6b0582dc38746b33b2454148c02349345412c6e743efb646d", size = 249093, upload-time = "2025-11-18T13:32:52.554Z" }, + { url = "https://files.pythonhosted.org/packages/76/b6/67d7c0e1f400b32c883e9342de4a8c2ae7c1a0b57c5de87622b7262e2309/coverage-7.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc13baf85cd8a4cfcf4a35c7bc9d795837ad809775f782f697bf630b7e200211", size = 251686, upload-time = "2025-11-18T13:32:54.862Z" }, + { url = "https://files.pythonhosted.org/packages/cc/75/b095bd4b39d49c3be4bffbb3135fea18a99a431c52dd7513637c0762fecb/coverage-7.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:099d11698385d572ceafb3288a5b80fe1fc58bf665b3f9d362389de488361d3d", size = 252930, upload-time = "2025-11-18T13:32:56.417Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f3/466f63015c7c80550bead3093aacabf5380c1220a2a93c35d374cae8f762/coverage-7.12.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:473dc45d69694069adb7680c405fb1e81f60b2aff42c81e2f2c3feaf544d878c", size = 249296, upload-time = "2025-11-18T13:32:58.074Z" }, + { url = "https://files.pythonhosted.org/packages/27/86/eba2209bf2b7e28c68698fc13437519a295b2d228ba9e0ec91673e09fa92/coverage-7.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:583f9adbefd278e9de33c33d6846aa8f5d164fa49b47144180a0e037f0688bb9", size = 251068, upload-time = "2025-11-18T13:32:59.646Z" }, + { url = "https://files.pythonhosted.org/packages/ec/55/ca8ae7dbba962a3351f18940b359b94c6bafdd7757945fdc79ec9e452dc7/coverage-7.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2089cc445f2dc0af6f801f0d1355c025b76c24481935303cf1af28f636688f0", size = 249034, upload-time = "2025-11-18T13:33:01.481Z" }, + { url = "https://files.pythonhosted.org/packages/7a/d7/39136149325cad92d420b023b5fd900dabdd1c3a0d1d5f148ef4a8cedef5/coverage-7.12.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:950411f1eb5d579999c5f66c62a40961f126fc71e5e14419f004471957b51508", size = 248853, upload-time = "2025-11-18T13:33:02.935Z" }, + { url = "https://files.pythonhosted.org/packages/fe/b6/76e1add8b87ef60e00643b0b7f8f7bb73d4bf5249a3be19ebefc5793dd25/coverage-7.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1aab7302a87bafebfe76b12af681b56ff446dc6f32ed178ff9c092ca776e6bc", size = 250619, upload-time = "2025-11-18T13:33:04.336Z" }, + { url = "https://files.pythonhosted.org/packages/95/87/924c6dc64f9203f7a3c1832a6a0eee5a8335dbe5f1bdadcc278d6f1b4d74/coverage-7.12.0-cp313-cp313-win32.whl", hash = "sha256:d7e0d0303c13b54db495eb636bc2465b2fb8475d4c8bcec8fe4b5ca454dfbae8", size = 220261, upload-time = "2025-11-18T13:33:06.493Z" }, + { url = "https://files.pythonhosted.org/packages/91/77/dd4aff9af16ff776bf355a24d87eeb48fc6acde54c907cc1ea89b14a8804/coverage-7.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:ce61969812d6a98a981d147d9ac583a36ac7db7766f2e64a9d4d059c2fe29d07", size = 221072, upload-time = "2025-11-18T13:33:07.926Z" }, + { url = "https://files.pythonhosted.org/packages/70/49/5c9dc46205fef31b1b226a6e16513193715290584317fd4df91cdaf28b22/coverage-7.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bcec6f47e4cb8a4c2dc91ce507f6eefc6a1b10f58df32cdc61dff65455031dfc", size = 219702, upload-time = "2025-11-18T13:33:09.631Z" }, + { url = "https://files.pythonhosted.org/packages/9b/62/f87922641c7198667994dd472a91e1d9b829c95d6c29529ceb52132436ad/coverage-7.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:459443346509476170d553035e4a3eed7b860f4fe5242f02de1010501956ce87", size = 218420, upload-time = "2025-11-18T13:33:11.153Z" }, + { url = "https://files.pythonhosted.org/packages/85/dd/1cc13b2395ef15dbb27d7370a2509b4aee77890a464fb35d72d428f84871/coverage-7.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04a79245ab2b7a61688958f7a855275997134bc84f4a03bc240cf64ff132abf6", size = 218773, upload-time = "2025-11-18T13:33:12.569Z" }, + { url = "https://files.pythonhosted.org/packages/74/40/35773cc4bb1e9d4658d4fb669eb4195b3151bef3bbd6f866aba5cd5dac82/coverage-7.12.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:09a86acaaa8455f13d6a99221d9654df249b33937b4e212b4e5a822065f12aa7", size = 260078, upload-time = "2025-11-18T13:33:14.037Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ee/231bb1a6ffc2905e396557585ebc6bdc559e7c66708376d245a1f1d330fc/coverage-7.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:907e0df1b71ba77463687a74149c6122c3f6aac56c2510a5d906b2f368208560", size = 262144, upload-time = "2025-11-18T13:33:15.601Z" }, + { url = "https://files.pythonhosted.org/packages/28/be/32f4aa9f3bf0b56f3971001b56508352c7753915345d45fab4296a986f01/coverage-7.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b57e2d0ddd5f0582bae5437c04ee71c46cd908e7bc5d4d0391f9a41e812dd12", size = 264574, upload-time = "2025-11-18T13:33:17.354Z" }, + { url = "https://files.pythonhosted.org/packages/68/7c/00489fcbc2245d13ab12189b977e0cf06ff3351cb98bc6beba8bd68c5902/coverage-7.12.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:58c1c6aa677f3a1411fe6fb28ec3a942e4f665df036a3608816e0847fad23296", size = 259298, upload-time = "2025-11-18T13:33:18.958Z" }, + { url = "https://files.pythonhosted.org/packages/96/b4/f0760d65d56c3bea95b449e02570d4abd2549dc784bf39a2d4721a2d8ceb/coverage-7.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4c589361263ab2953e3c4cd2a94db94c4ad4a8e572776ecfbad2389c626e4507", size = 262150, upload-time = "2025-11-18T13:33:20.644Z" }, + { url = "https://files.pythonhosted.org/packages/c5/71/9a9314df00f9326d78c1e5a910f520d599205907432d90d1c1b7a97aa4b1/coverage-7.12.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:91b810a163ccad2e43b1faa11d70d3cf4b6f3d83f9fd5f2df82a32d47b648e0d", size = 259763, upload-time = "2025-11-18T13:33:22.189Z" }, + { url = "https://files.pythonhosted.org/packages/10/34/01a0aceed13fbdf925876b9a15d50862eb8845454301fe3cdd1df08b2182/coverage-7.12.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:40c867af715f22592e0d0fb533a33a71ec9e0f73a6945f722a0c85c8c1cbe3a2", size = 258653, upload-time = "2025-11-18T13:33:24.239Z" }, + { url = "https://files.pythonhosted.org/packages/8d/04/81d8fd64928acf1574bbb0181f66901c6c1c6279c8ccf5f84259d2c68ae9/coverage-7.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:68b0d0a2d84f333de875666259dadf28cc67858bc8fd8b3f1eae84d3c2bec455", size = 260856, upload-time = "2025-11-18T13:33:26.365Z" }, + { url = "https://files.pythonhosted.org/packages/f2/76/fa2a37bfaeaf1f766a2d2360a25a5297d4fb567098112f6517475eee120b/coverage-7.12.0-cp313-cp313t-win32.whl", hash = "sha256:73f9e7fbd51a221818fd11b7090eaa835a353ddd59c236c57b2199486b116c6d", size = 220936, upload-time = "2025-11-18T13:33:28.165Z" }, + { url = "https://files.pythonhosted.org/packages/f9/52/60f64d932d555102611c366afb0eb434b34266b1d9266fc2fe18ab641c47/coverage-7.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:24cff9d1f5743f67db7ba46ff284018a6e9aeb649b67aa1e70c396aa1b7cb23c", size = 222001, upload-time = "2025-11-18T13:33:29.656Z" }, + { url = "https://files.pythonhosted.org/packages/77/df/c303164154a5a3aea7472bf323b7c857fed93b26618ed9fc5c2955566bb0/coverage-7.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c87395744f5c77c866d0f5a43d97cc39e17c7f1cb0115e54a2fe67ca75c5d14d", size = 220273, upload-time = "2025-11-18T13:33:31.415Z" }, + { url = "https://files.pythonhosted.org/packages/bf/2e/fc12db0883478d6e12bbd62d481210f0c8daf036102aa11434a0c5755825/coverage-7.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a1c59b7dc169809a88b21a936eccf71c3895a78f5592051b1af8f4d59c2b4f92", size = 217777, upload-time = "2025-11-18T13:33:32.86Z" }, + { url = "https://files.pythonhosted.org/packages/1f/c1/ce3e525d223350c6ec16b9be8a057623f54226ef7f4c2fee361ebb6a02b8/coverage-7.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8787b0f982e020adb732b9f051f3e49dd5054cebbc3f3432061278512a2b1360", size = 218100, upload-time = "2025-11-18T13:33:34.532Z" }, + { url = "https://files.pythonhosted.org/packages/15/87/113757441504aee3808cb422990ed7c8bcc2d53a6779c66c5adef0942939/coverage-7.12.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ea5a9f7dc8877455b13dd1effd3202e0bca72f6f3ab09f9036b1bcf728f69ac", size = 249151, upload-time = "2025-11-18T13:33:36.135Z" }, + { url = "https://files.pythonhosted.org/packages/d9/1d/9529d9bd44049b6b05bb319c03a3a7e4b0a8a802d28fa348ad407e10706d/coverage-7.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fdba9f15849534594f60b47c9a30bc70409b54947319a7c4fd0e8e3d8d2f355d", size = 251667, upload-time = "2025-11-18T13:33:37.996Z" }, + { url = "https://files.pythonhosted.org/packages/11/bb/567e751c41e9c03dc29d3ce74b8c89a1e3396313e34f255a2a2e8b9ebb56/coverage-7.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a00594770eb715854fb1c57e0dea08cce6720cfbc531accdb9850d7c7770396c", size = 253003, upload-time = "2025-11-18T13:33:39.553Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b3/c2cce2d8526a02fb9e9ca14a263ca6fc074449b33a6afa4892838c903528/coverage-7.12.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5560c7e0d82b42eb1951e4f68f071f8017c824ebfd5a6ebe42c60ac16c6c2434", size = 249185, upload-time = "2025-11-18T13:33:42.086Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a7/967f93bb66e82c9113c66a8d0b65ecf72fc865adfba5a145f50c7af7e58d/coverage-7.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2e26b481c9159c2773a37947a9718cfdc58893029cdfb177531793e375cfc", size = 251025, upload-time = "2025-11-18T13:33:43.634Z" }, + { url = "https://files.pythonhosted.org/packages/b9/b2/f2f6f56337bc1af465d5b2dc1ee7ee2141b8b9272f3bf6213fcbc309a836/coverage-7.12.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6e1a8c066dabcde56d5d9fed6a66bc19a2883a3fe051f0c397a41fc42aedd4cc", size = 248979, upload-time = "2025-11-18T13:33:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7a/bf4209f45a4aec09d10a01a57313a46c0e0e8f4c55ff2965467d41a92036/coverage-7.12.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f7ba9da4726e446d8dd8aae5a6cd872511184a5d861de80a86ef970b5dacce3e", size = 248800, upload-time = "2025-11-18T13:33:47.546Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b7/1e01b8696fb0521810f60c5bbebf699100d6754183e6cc0679bf2ed76531/coverage-7.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e0f483ab4f749039894abaf80c2f9e7ed77bbf3c737517fb88c8e8e305896a17", size = 250460, upload-time = "2025-11-18T13:33:49.537Z" }, + { url = "https://files.pythonhosted.org/packages/71/ae/84324fb9cb46c024760e706353d9b771a81b398d117d8c1fe010391c186f/coverage-7.12.0-cp314-cp314-win32.whl", hash = "sha256:76336c19a9ef4a94b2f8dc79f8ac2da3f193f625bb5d6f51a328cd19bfc19933", size = 220533, upload-time = "2025-11-18T13:33:51.16Z" }, + { url = "https://files.pythonhosted.org/packages/e2/71/1033629deb8460a8f97f83e6ac4ca3b93952e2b6f826056684df8275e015/coverage-7.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c1059b600aec6ef090721f8f633f60ed70afaffe8ecab85b59df748f24b31fe", size = 221348, upload-time = "2025-11-18T13:33:52.776Z" }, + { url = "https://files.pythonhosted.org/packages/0a/5f/ac8107a902f623b0c251abdb749be282dc2ab61854a8a4fcf49e276fce2f/coverage-7.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:172cf3a34bfef42611963e2b661302a8931f44df31629e5b1050567d6b90287d", size = 219922, upload-time = "2025-11-18T13:33:54.316Z" }, + { url = "https://files.pythonhosted.org/packages/79/6e/f27af2d4da367f16077d21ef6fe796c874408219fa6dd3f3efe7751bd910/coverage-7.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:aa7d48520a32cb21c7a9b31f81799e8eaec7239db36c3b670be0fa2403828d1d", size = 218511, upload-time = "2025-11-18T13:33:56.343Z" }, + { url = "https://files.pythonhosted.org/packages/67/dd/65fd874aa460c30da78f9d259400d8e6a4ef457d61ab052fd248f0050558/coverage-7.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:90d58ac63bc85e0fb919f14d09d6caa63f35a5512a2205284b7816cafd21bb03", size = 218771, upload-time = "2025-11-18T13:33:57.966Z" }, + { url = "https://files.pythonhosted.org/packages/55/e0/7c6b71d327d8068cb79c05f8f45bf1b6145f7a0de23bbebe63578fe5240a/coverage-7.12.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca8ecfa283764fdda3eae1bdb6afe58bf78c2c3ec2b2edcb05a671f0bba7b3f9", size = 260151, upload-time = "2025-11-18T13:33:59.597Z" }, + { url = "https://files.pythonhosted.org/packages/49/ce/4697457d58285b7200de6b46d606ea71066c6e674571a946a6ea908fb588/coverage-7.12.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:874fe69a0785d96bd066059cd4368022cebbec1a8958f224f0016979183916e6", size = 262257, upload-time = "2025-11-18T13:34:01.166Z" }, + { url = "https://files.pythonhosted.org/packages/2f/33/acbc6e447aee4ceba88c15528dbe04a35fb4d67b59d393d2e0d6f1e242c1/coverage-7.12.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3c889c0b8b283a24d721a9eabc8ccafcfc3aebf167e4cd0d0e23bf8ec4e339", size = 264671, upload-time = "2025-11-18T13:34:02.795Z" }, + { url = "https://files.pythonhosted.org/packages/87/ec/e2822a795c1ed44d569980097be839c5e734d4c0c1119ef8e0a073496a30/coverage-7.12.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bb5b894b3ec09dcd6d3743229dc7f2c42ef7787dc40596ae04c0edda487371e", size = 259231, upload-time = "2025-11-18T13:34:04.397Z" }, + { url = "https://files.pythonhosted.org/packages/72/c5/a7ec5395bb4a49c9b7ad97e63f0c92f6bf4a9e006b1393555a02dae75f16/coverage-7.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:79a44421cd5fba96aa57b5e3b5a4d3274c449d4c622e8f76882d76635501fd13", size = 262137, upload-time = "2025-11-18T13:34:06.068Z" }, + { url = "https://files.pythonhosted.org/packages/67/0c/02c08858b764129f4ecb8e316684272972e60777ae986f3865b10940bdd6/coverage-7.12.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:33baadc0efd5c7294f436a632566ccc1f72c867f82833eb59820ee37dc811c6f", size = 259745, upload-time = "2025-11-18T13:34:08.04Z" }, + { url = "https://files.pythonhosted.org/packages/5a/04/4fd32b7084505f3829a8fe45c1a74a7a728cb251aaadbe3bec04abcef06d/coverage-7.12.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c406a71f544800ef7e9e0000af706b88465f3573ae8b8de37e5f96c59f689ad1", size = 258570, upload-time = "2025-11-18T13:34:09.676Z" }, + { url = "https://files.pythonhosted.org/packages/48/35/2365e37c90df4f5342c4fa202223744119fe31264ee2924f09f074ea9b6d/coverage-7.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e71bba6a40883b00c6d571599b4627f50c360b3d0d02bfc658168936be74027b", size = 260899, upload-time = "2025-11-18T13:34:11.259Z" }, + { url = "https://files.pythonhosted.org/packages/05/56/26ab0464ca733fa325e8e71455c58c1c374ce30f7c04cebb88eabb037b18/coverage-7.12.0-cp314-cp314t-win32.whl", hash = "sha256:9157a5e233c40ce6613dead4c131a006adfda70e557b6856b97aceed01b0e27a", size = 221313, upload-time = "2025-11-18T13:34:12.863Z" }, + { url = "https://files.pythonhosted.org/packages/da/1c/017a3e1113ed34d998b27d2c6dba08a9e7cb97d362f0ec988fcd873dcf81/coverage-7.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e84da3a0fd233aeec797b981c51af1cabac74f9bd67be42458365b30d11b5291", size = 222423, upload-time = "2025-11-18T13:34:15.14Z" }, + { url = "https://files.pythonhosted.org/packages/4c/36/bcc504fdd5169301b52568802bb1b9cdde2e27a01d39fbb3b4b508ab7c2c/coverage-7.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:01d24af36fedda51c2b1aca56e4330a3710f83b02a5ff3743a6b015ffa7c9384", size = 220459, upload-time = "2025-11-18T13:34:17.222Z" }, + { url = "https://files.pythonhosted.org/packages/ce/a3/43b749004e3c09452e39bb56347a008f0a0668aad37324a99b5c8ca91d9e/coverage-7.12.0-py3-none-any.whl", hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a", size = 209503, upload-time = "2025-11-18T13:34:18.892Z" }, ] [[package]] @@ -571,19 +587,19 @@ wheels = [ [[package]] name = "faker" -version = "37.12.0" +version = "38.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/84/e95acaa848b855e15c83331d0401ee5f84b2f60889255c2e055cb4fb6bdf/faker-37.12.0.tar.gz", hash = "sha256:7505e59a7e02fa9010f06c3e1e92f8250d4cfbb30632296140c2d6dbef09b0fa", size = 1935741, upload-time = "2025-10-24T15:19:58.764Z" } +sdist = { url = "https://files.pythonhosted.org/packages/64/27/022d4dbd4c20567b4c294f79a133cc2f05240ea61e0d515ead18c995c249/faker-38.2.0.tar.gz", hash = "sha256:20672803db9c7cb97f9b56c18c54b915b6f1d8991f63d1d673642dc43f5ce7ab", size = 1941469, upload-time = "2025-11-19T16:37:31.892Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/98/2c050dec90e295a524c9b65c4cb9e7c302386a296b2938710448cbd267d5/faker-37.12.0-py3-none-any.whl", hash = "sha256:afe7ccc038da92f2fbae30d8e16d19d91e92e242f8401ce9caf44de892bab4c4", size = 1975461, upload-time = "2025-10-24T15:19:55.739Z" }, + { url = "https://files.pythonhosted.org/packages/17/93/00c94d45f55c336434a15f98d906387e87ce28f9918e4444829a8fda432d/faker-38.2.0-py3-none-any.whl", hash = "sha256:35fe4a0a79dee0dc4103a6083ee9224941e7d3594811a50e3969e547b0d2ee65", size = 1980505, upload-time = "2025-11-19T16:37:30.208Z" }, ] [[package]] name = "fastapi" -version = "0.121.0" +version = "0.122.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -591,9 +607,9 @@ dependencies = [ { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/e3/77a2df0946703973b9905fd0cde6172c15e0781984320123b4f5079e7113/fastapi-0.121.0.tar.gz", hash = "sha256:06663356a0b1ee93e875bbf05a31fb22314f5bed455afaaad2b2dad7f26e98fa", size = 342412, upload-time = "2025-11-03T10:25:54.818Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/de/3ee97a4f6ffef1fb70bf20561e4f88531633bb5045dc6cebc0f8471f764d/fastapi-0.122.0.tar.gz", hash = "sha256:cd9b5352031f93773228af8b4c443eedc2ac2aa74b27780387b853c3726fb94b", size = 346436, upload-time = "2025-11-24T19:17:47.95Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/2c/42277afc1ba1a18f8358561eee40785d27becab8f80a1f945c0a3051c6eb/fastapi-0.121.0-py3-none-any.whl", hash = "sha256:8bdf1b15a55f4e4b0d6201033da9109ea15632cb76cf156e7b8b4019f2172106", size = 109183, upload-time = "2025-11-03T10:25:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7a/93/aa8072af4ff37b795f6bbf43dcaf61115f40f49935c7dbb180c9afc3f421/fastapi-0.122.0-py3-none-any.whl", hash = "sha256:a456e8915dfc6c8914a50d9651133bd47ec96d331c5b44600baa635538a30d67", size = 110671, upload-time = "2025-11-24T19:17:45.96Z" }, ] [package.optional-dependencies] @@ -608,16 +624,16 @@ standard = [ [[package]] name = "fastapi-cli" -version = "0.0.14" +version = "0.0.16" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "rich-toolkit" }, { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/13/11e43d630be84e51ba5510a6da6a11eb93b44b72caa796137c5dddda937b/fastapi_cli-0.0.14.tar.gz", hash = "sha256:ddfb5de0a67f77a8b3271af1460489bd4d7f4add73d11fbfac613827b0275274", size = 17994, upload-time = "2025-10-20T16:33:21.054Z" } +sdist = { url = "https://files.pythonhosted.org/packages/99/75/9407a6b452be4c988feacec9c9d2f58d8f315162a6c7258d5a649d933ebe/fastapi_cli-0.0.16.tar.gz", hash = "sha256:e8a2a1ecf7a4e062e3b2eec63ae34387d1e142d4849181d936b23c4bdfe29073", size = 19447, upload-time = "2025-11-10T19:01:07.856Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/e8/bc8bbfd93dcc8e347ce98a3e654fb0d2e5f2739afb46b98f41a30c339269/fastapi_cli-0.0.14-py3-none-any.whl", hash = "sha256:e66b9ad499ee77a4e6007545cde6de1459b7f21df199d7f29aad2adaab168eca", size = 11151, upload-time = "2025-10-20T16:33:19.318Z" }, + { url = "https://files.pythonhosted.org/packages/55/43/678528c19318394320ee43757648d5e0a8070cf391b31f69d931e5c840d2/fastapi_cli-0.0.16-py3-none-any.whl", hash = "sha256:addcb6d130b5b9c91adbbf3f2947fe115991495fdb442fe3e51b5fc6327df9f4", size = 12312, upload-time = "2025-11-10T19:01:06.728Z" }, ] [package.optional-dependencies] @@ -628,9 +644,10 @@ standard = [ [[package]] name = "fastapi-cloud-cli" -version = "0.3.1" +version = "0.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "fastar" }, { name = "httpx" }, { name = "pydantic", extra = ["email"] }, { name = "rich-toolkit" }, @@ -639,9 +656,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f9/48/0f14d8555b750dc8c04382804e4214f1d7f55298127f3a0237ba566e69dd/fastapi_cloud_cli-0.3.1.tar.gz", hash = "sha256:8c7226c36e92e92d0c89827e8f56dbf164ab2de4444bd33aa26b6c3f7675db69", size = 24080, upload-time = "2025-10-09T11:32:58.174Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/8d/cb1ae52121190eb75178b146652bfdce9296d2fd19aa30410ebb1fab3a63/fastapi_cloud_cli-0.5.1.tar.gz", hash = "sha256:5ed9591fda9ef5ed846c7fb937a06c491a00eef6d5bb656c84d82f47e500804b", size = 30746, upload-time = "2025-11-20T16:53:24.491Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/79/7f5a5e5513e6a737e5fb089d9c59c74d4d24dc24d581d3aa519b326bedda/fastapi_cloud_cli-0.3.1-py3-none-any.whl", hash = "sha256:7d1a98a77791a9d0757886b2ffbf11bcc6b3be93210dd15064be10b216bf7e00", size = 19711, upload-time = "2025-10-09T11:32:57.118Z" }, + { url = "https://files.pythonhosted.org/packages/42/d6/b83f0801fd2c3f648e3696cdd2a1967b176f43c0c9db35c0350a67e7c141/fastapi_cloud_cli-0.5.1-py3-none-any.whl", hash = "sha256:1a28415b059b27af180a55a835ac2c9e924a66be88412d5649d4f91993d1a698", size = 23216, upload-time = "2025-11-20T16:53:23.119Z" }, ] [[package]] @@ -748,6 +765,59 @@ dependencies = [ { name = "sqlmodel" }, ] +[[package]] +name = "fastar" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/7e/0563141e374012f47eb0d219323378f4207d15d9939fa7aa0fa404d8613d/fastar-0.7.0.tar.gz", hash = "sha256:76739b48121cf8601ecc3ea9e87858362774b53cc1dd7e8332696b99c6ad2c27", size = 67917, upload-time = "2025-11-24T15:52:37.072Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/82/96043bd83b54f2074a7f47df7ad912b6de26b398a424580167a0d059b46e/fastar-0.7.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d66c09da9ed60536326783becab08db4d4f478e12c0543e7ac750336e72b38e5", size = 705365, upload-time = "2025-11-24T15:51:14.945Z" }, + { url = "https://files.pythonhosted.org/packages/66/01/24f42e7693713c41b389aaa15c0f010ac84eeb9dd5e4e2e0336386b2cef6/fastar-0.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4e443363617551be2e48f87a63f42ba1275c8f42094c6616168bd0512c9ed9b9", size = 627848, upload-time = "2025-11-24T15:51:00.295Z" }, + { url = "https://files.pythonhosted.org/packages/2e/5a/03d2589e2652506e73a8a85312852b5d3263ca348912fc39a396968009ff/fastar-0.7.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5a6981f162ebf1148c08668e1ab0fa58f4a6b32a0a126545042a859d836e54ec", size = 867646, upload-time = "2025-11-24T15:50:30.874Z" }, + { url = "https://files.pythonhosted.org/packages/dd/81/ac6f2484f8919b642a45088d487089ac926f74d9b12f347e4ed2e3ebaf8e/fastar-0.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7605ce63582432f2bc6b5e59e569b818f5db74506d452be609537a5699cedc19", size = 763982, upload-time = "2025-11-24T15:49:31.069Z" }, + { url = "https://files.pythonhosted.org/packages/eb/77/0ab5991e97e882a90043f287ba08124b8b0a2af4e68e3e8e77cb6e9b09ab/fastar-0.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9ae8c4dec44bac4a3e763d5993191962db1285525da61154b6bc158ebcd01ba4", size = 763680, upload-time = "2025-11-24T15:49:46.938Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b4/0c269f4136278e0c652f7d6eca57e71104d02ba1fc3ebf7057a6c36e8339/fastar-0.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:abe4ff6fcc353618e395cceb760ae3a90d19686c2d67c9d6654ec0fa9d265395", size = 930118, upload-time = "2025-11-24T15:50:01.681Z" }, + { url = "https://files.pythonhosted.org/packages/70/11/f62a4b652534a5e4f3303b4124e9ca55864f77de9f74588643332f4e5caf/fastar-0.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b54bbb9aa12b2c5550dfafedfe664088bc22a8acc4eebcc9dff7a1ca3048216", size = 820641, upload-time = "2025-11-24T15:50:15.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c6/669c167472d31ea94caa5afa75227ef6f123e3be8474f56f9dad01c9b8d8/fastar-0.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f434f0a91235aec22a1d39714af3283ef768bb2de548e61ee4f3a74fb3504a2e", size = 820106, upload-time = "2025-11-24T15:50:45.978Z" }, + { url = "https://files.pythonhosted.org/packages/1d/7a/305c99ff3708fc3cb6bebbc2f6469d3c3c4f51119306691d0f57283da0d2/fastar-0.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:400e48ca94e5ed9a1f4d17dd8e74cbd9a978de4ba718f5610c73ba6172dcc59b", size = 985425, upload-time = "2025-11-24T15:51:31.58Z" }, + { url = "https://files.pythonhosted.org/packages/c7/c5/04ab4db328d0e3193cf9b1bbc3147f98cf09e1f99c24906789b929198fa8/fastar-0.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:94b11ba3d9d23fe612a4a612a62d7b2f18e2d7a1be2d5f94b938448a906436e9", size = 1038104, upload-time = "2025-11-24T15:51:49.085Z" }, + { url = "https://files.pythonhosted.org/packages/e6/72/e7c7d684efe1b92062096c29d0d5b38ca549beb5eb35336acf212a90ddc8/fastar-0.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9610f6edb6fdb627491148e7071f725b4abffb8655554cad6a45637772f0795a", size = 1044294, upload-time = "2025-11-24T15:52:06.47Z" }, + { url = "https://files.pythonhosted.org/packages/e6/11/b2ad21f1b8ac20b6c4676e83f2dd3c5f70ff9a9926df60c3f4e36be8be08/fastar-0.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:db2373ebe1a699ce3ea34296ab85a22a572667aefd198ca6fa32fee5e69970fc", size = 993265, upload-time = "2025-11-24T15:52:24.049Z" }, + { url = "https://files.pythonhosted.org/packages/03/38/d44a7ea41c407d46c56f160fb870536e1dd9ba01c44b46d7091835ff1719/fastar-0.7.0-cp313-cp313-win32.whl", hash = "sha256:bcb4f04daa574108092abfba8c0f747e65910464671d5ab72e6f55d19f7e2a71", size = 455032, upload-time = "2025-11-24T15:53:03.244Z" }, + { url = "https://files.pythonhosted.org/packages/9d/65/d86c8d53b4f00bb7eed9c89eda2801d33930a8729dac72838807eb2d7314/fastar-0.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:a577121830ba14acd70a8eccc7a0f815a78e9f01981bc9b71a005caa08f63afa", size = 489446, upload-time = "2025-11-24T15:52:50.877Z" }, + { url = "https://files.pythonhosted.org/packages/04/6d/12bc62cd7a425747efbba0755cbfd23015d592c3bf85753442ff1283bfc6/fastar-0.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:b4e0ddd1fb513eac866eca22323dd28b2671aaa3facd10a854d3beef4933372b", size = 460203, upload-time = "2025-11-24T15:52:41.739Z" }, + { url = "https://files.pythonhosted.org/packages/3f/a5/a5eff2a7fe21026cce5fa3a175d88a23a34bca461cddeab87042c2c47e82/fastar-0.7.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:7cc47eeac659fed55f547b6c84fbd302726fab64de720c96d3ddcf0952535d0e", size = 705379, upload-time = "2025-11-24T15:51:16.497Z" }, + { url = "https://files.pythonhosted.org/packages/00/06/67228a6e1b32414afe79510ba1256b791541b8801d12660d6fbb203c88b7/fastar-0.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f3139c8d48bdb2c2d79a42eb940efc20e67e1b9dd26798257b71f0d9f0083a5a", size = 627905, upload-time = "2025-11-24T15:51:01.523Z" }, + { url = "https://files.pythonhosted.org/packages/ea/11/753fd5b766d5b170d6d47ebb31aee87b95f5e5776e2661132aae68cae51a/fastar-0.7.0-cp314-cp314-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f0e2c86b690116f50bd40c444fce6da000695e558a94e460d8b46eff6f23b26f", size = 868266, upload-time = "2025-11-24T15:50:32.119Z" }, + { url = "https://files.pythonhosted.org/packages/40/66/70a191f4d61df4bcda77e759bb840d3cdda796ff26628a454ca44ef58158/fastar-0.7.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a698533c59125856e1c14978c589f933de312f066f2a15978f11030807ac535", size = 763815, upload-time = "2025-11-24T15:49:32.214Z" }, + { url = "https://files.pythonhosted.org/packages/d2/a0/72e7886ec7dd16e523522253ecf1862e422e43e3142de29052a562b6499d/fastar-0.7.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:240c546a20b6f8c1edfe0ab40ac6113cecea02380d6f59e6f9be3d1e079d0767", size = 763288, upload-time = "2025-11-24T15:49:48.082Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b5/0d1cc3356bba8afad036e1808dc10ca76341cafd681a4479c98eb37d947f/fastar-0.7.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f37e415192a27980377c0a0859275f178bfcd54c3b972f2f273bee1276a75f1", size = 929296, upload-time = "2025-11-24T15:50:02.957Z" }, + { url = "https://files.pythonhosted.org/packages/59/79/21aa7f864e2e3a1e7244475f864cd82d34b86aac73b1f54c8eb32778c34e/fastar-0.7.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c865328d56525fc71441f848dcf3d9d20855f3f619c4dca99ecdd932c7e0160c", size = 820264, upload-time = "2025-11-24T15:50:16.91Z" }, + { url = "https://files.pythonhosted.org/packages/de/91/c576af124855de6ffbb48511625ff51653029ba0fde8d3ef6913cf0f968c/fastar-0.7.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a9e11313551a10032a6cd97c27434fde6a858794257d709040a7b351b586fe4", size = 819896, upload-time = "2025-11-24T15:50:47.264Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f1/3b3ada104c1924f0a78bc66f89a1bca4957c26e7ad5befaaa2f4701af7bb/fastar-0.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f0532d5ef74d0262f998150a7a2e5d8e51f411d400f655c5a83eb8775fc8d5ab", size = 985552, upload-time = "2025-11-24T15:51:32.859Z" }, + { url = "https://files.pythonhosted.org/packages/c1/1f/1f6424bc8bc2cdc932b16670433b4368b09bf32872b9975c1c1cba02891e/fastar-0.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:008930f99c7602da1ec820b165724621df8d6ca327d8877bd46f3600c848aae0", size = 1038126, upload-time = "2025-11-24T15:51:50.93Z" }, + { url = "https://files.pythonhosted.org/packages/09/8e/f4c4db8de826ea9ff134c6bc9bf2aaf1fc977eac9153b3356f6d181a3149/fastar-0.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6965219b0dbb897557617400ef3a21601a08cfac0ba0e0dfcdbde19a13e0769d", size = 1044273, upload-time = "2025-11-24T15:52:08.061Z" }, + { url = "https://files.pythonhosted.org/packages/71/c6/b1af54e78ea288144bbb1e2e7b2ad56342285029bb2b68f84bf8c8713d70/fastar-0.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bcf277df3c357db68b422944aa3717aff6178c797c4c64711437a81fc2271552", size = 993779, upload-time = "2025-11-24T15:52:25.818Z" }, + { url = "https://files.pythonhosted.org/packages/7f/25/f3043ebd1e19bb262a0ff7a2f2a07945e5e912ace308202e0f89b1d7f96c/fastar-0.7.0-cp314-cp314-win32.whl", hash = "sha256:12cff2cc933e4a74e56c591b1dda06cdae23c0718d07cdb696701e3596a23c5e", size = 455711, upload-time = "2025-11-24T15:53:05.198Z" }, + { url = "https://files.pythonhosted.org/packages/f9/13/b691a58b3cb1567c95b60032009549ccebcefabeceb6c3c4a6a3bddf9253/fastar-0.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:99e7d8928b1d7092053e40d9132a246b4ed8156fa3cecad3def3ea5b2fd24027", size = 489799, upload-time = "2025-11-24T15:52:52.552Z" }, + { url = "https://files.pythonhosted.org/packages/14/0e/7c907f00cb71abc56b1dc3d4aaeaee85061feb955f014ac75af9933f7895/fastar-0.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:cedf4212173f502fc61883a76142ccad9d9cbd2b61f0704d36b7bf6a17df311d", size = 460748, upload-time = "2025-11-24T15:52:43.105Z" }, + { url = "https://files.pythonhosted.org/packages/d5/97/a4cc30a5a962fe23e0b21937fb99ca5a267aa6dee1e3dd72df853a758cb0/fastar-0.7.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8484b7c55d77874d272c236869855021376722d9c51ff5747ad8b42896b6c4df", size = 704853, upload-time = "2025-11-24T15:51:17.708Z" }, + { url = "https://files.pythonhosted.org/packages/0e/4e/02312660f6027f5ad2bb75e16ea5f2a9f89439e0a502c754b4d8eff0beb1/fastar-0.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:514947a8d057e111a9ffd5943ce740d4186f9084562b44cc9875fa39b1a2e109", size = 626773, upload-time = "2025-11-24T15:51:02.835Z" }, + { url = "https://files.pythonhosted.org/packages/61/c7/e04147583ca17fbe6970dc20083b2a38e2ffc2e4e4f76d4e7640c0dbfa49/fastar-0.7.0-cp314-cp314t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1b71a5eb92f0c730798896e512a75f96b267bfd610b1148a8348dbcd565dea6c", size = 867940, upload-time = "2025-11-24T15:50:33.402Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c1/8316762971c117b8043202d531320b3ebb740fc02bc5208e8a734e7d5b3c/fastar-0.7.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fce1bfa66ceb0e96b6eee89f9efb3250929df22fdfdab8a08735c09b50cfe0c", size = 762971, upload-time = "2025-11-24T15:49:33.406Z" }, + { url = "https://files.pythonhosted.org/packages/62/07/d394742e2892818d52f391d40d24d60ef9a214865fef4a9e55339022d990/fastar-0.7.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9632c25c6a85f5eab589437bc6bfbb5461f93b799882e3c750b6f86448ad9ede", size = 762796, upload-time = "2025-11-24T15:49:49.187Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7d/bb3ab1f10500c765833fc2c931d11e3fa2dae5e42e0451af759a89b5ef57/fastar-0.7.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c45e2422cca8fd3b5509edf8db44cceeb0d4eed3cc12d90d91d0e1ea08034258", size = 929810, upload-time = "2025-11-24T15:50:04.166Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cb/5e42841f52a65b02796bae27a484c23375eabb07750c88face71d82e3717/fastar-0.7.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99836a00322c39689f7d9772662a7b5ee62b3ec1a344ad693f9c162226775039", size = 819858, upload-time = "2025-11-24T15:50:18.395Z" }, + { url = "https://files.pythonhosted.org/packages/0e/7e/e268246b4f38421c84bb42048311fe269feacd8e1d5a6cac48b0f64f8044/fastar-0.7.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcd2756c2ae9f1374619207b98d1143c9865910c9fecd094c8656b95c5a9a45b", size = 819585, upload-time = "2025-11-24T15:50:48.488Z" }, + { url = "https://files.pythonhosted.org/packages/50/1f/3d05285c98d3245944540aec77364618e0f508d0c4bbf311a7762b644c35/fastar-0.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3ced9eddb9adcf8b27361c180f6bdfbc8cb2e36479aa00e4e7e78c17c7768efc", size = 984526, upload-time = "2025-11-24T15:51:34.988Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e0/34c114c7016901cac190b18871212f7433871470d1ba1c92ed891ae7d85f/fastar-0.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:39ba9256790a13289f986c07c73bbc075647337008f1faea104e5e013a17ee70", size = 1037651, upload-time = "2025-11-24T15:51:52.286Z" }, + { url = "https://files.pythonhosted.org/packages/39/7e/371ddb9ed65733aa51370bf774234a142d315f841538c7af7fd959cc5c5e/fastar-0.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f445e1acb722e228364c2d8012e6be1b46502062e3638cbe5b98c7c2d6bebb72", size = 1044369, upload-time = "2025-11-24T15:52:10.031Z" }, + { url = "https://files.pythonhosted.org/packages/92/0f/0d6a9fab23ba227f79f2e728aef274daf8fe8148c7cbd58022b752af7aeb/fastar-0.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1e9b1e0cb44b0d43dae153d80e519b04aa0bc4c98240d4a2d85c7ede13b37aae", size = 993840, upload-time = "2025-11-24T15:52:27.41Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e2/df1c197e4bfca4c23114ab1251c70b70a9a7a427a1ab73bef2dd9750056a/fastar-0.7.0-cp314-cp314t-win32.whl", hash = "sha256:44956db52c2d6afa5a26a9d2c8e926eb55902a9151ab0ce0bfa3023479db4800", size = 454334, upload-time = "2025-11-24T15:53:09.556Z" }, + { url = "https://files.pythonhosted.org/packages/ee/b0/e2b55bb0b521ac9abada459cd2bce8488b36525f913af536bf1dec90dc03/fastar-0.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:cfd514372850774e8651c4e98b2b81bba0ae00f2e1dfa666da89ea5e02d1e61a", size = 489047, upload-time = "2025-11-24T15:52:57.327Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c1/ea150ccd09a6247a65e162596db393fb642ad92bf7d2af9f7e4ae58233da/fastar-0.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:96a366565662567ba1b7c1d2f72e02584575a33b220c361707e168270b68d4e4", size = 459525, upload-time = "2025-11-24T15:52:44.492Z" }, +] + [[package]] name = "filelock" version = "3.20.0" @@ -1128,54 +1198,54 @@ wheels = [ [[package]] name = "numpy" -version = "2.3.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/f4/098d2270d52b41f1bd7db9fc288aaa0400cb48c2a3e2af6fa365d9720947/numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a", size = 20582187, upload-time = "2025-10-15T16:18:11.77Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/7e/b72610cc91edf138bc588df5150957a4937221ca6058b825b4725c27be62/numpy-2.3.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c090d4860032b857d94144d1a9976b8e36709e40386db289aaf6672de2a81966", size = 20950335, upload-time = "2025-10-15T16:16:10.304Z" }, - { url = "https://files.pythonhosted.org/packages/3e/46/bdd3370dcea2f95ef14af79dbf81e6927102ddf1cc54adc0024d61252fd9/numpy-2.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a13fc473b6db0be619e45f11f9e81260f7302f8d180c49a22b6e6120022596b3", size = 14179878, upload-time = "2025-10-15T16:16:12.595Z" }, - { url = "https://files.pythonhosted.org/packages/ac/01/5a67cb785bda60f45415d09c2bc245433f1c68dd82eef9c9002c508b5a65/numpy-2.3.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:3634093d0b428e6c32c3a69b78e554f0cd20ee420dcad5a9f3b2a63762ce4197", size = 5108673, upload-time = "2025-10-15T16:16:14.877Z" }, - { url = "https://files.pythonhosted.org/packages/c2/cd/8428e23a9fcebd33988f4cb61208fda832800ca03781f471f3727a820704/numpy-2.3.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:043885b4f7e6e232d7df4f51ffdef8c36320ee9d5f227b380ea636722c7ed12e", size = 6641438, upload-time = "2025-10-15T16:16:16.805Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d1/913fe563820f3c6b079f992458f7331278dcd7ba8427e8e745af37ddb44f/numpy-2.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7", size = 14281290, upload-time = "2025-10-15T16:16:18.764Z" }, - { url = "https://files.pythonhosted.org/packages/9e/7e/7d306ff7cb143e6d975cfa7eb98a93e73495c4deabb7d1b5ecf09ea0fd69/numpy-2.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc8a63918b04b8571789688b2780ab2b4a33ab44bfe8ccea36d3eba51228c953", size = 16636543, upload-time = "2025-10-15T16:16:21.072Z" }, - { url = "https://files.pythonhosted.org/packages/47/6a/8cfc486237e56ccfb0db234945552a557ca266f022d281a2f577b98e955c/numpy-2.3.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40cc556d5abbc54aabe2b1ae287042d7bdb80c08edede19f0c0afb36ae586f37", size = 16056117, upload-time = "2025-10-15T16:16:23.369Z" }, - { url = "https://files.pythonhosted.org/packages/b1/0e/42cb5e69ea901e06ce24bfcc4b5664a56f950a70efdcf221f30d9615f3f3/numpy-2.3.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ecb63014bb7f4ce653f8be7f1df8cbc6093a5a2811211770f6606cc92b5a78fd", size = 18577788, upload-time = "2025-10-15T16:16:27.496Z" }, - { url = "https://files.pythonhosted.org/packages/86/92/41c3d5157d3177559ef0a35da50f0cda7fa071f4ba2306dd36818591a5bc/numpy-2.3.4-cp313-cp313-win32.whl", hash = "sha256:e8370eb6925bb8c1c4264fec52b0384b44f675f191df91cbe0140ec9f0955646", size = 6282620, upload-time = "2025-10-15T16:16:29.811Z" }, - { url = "https://files.pythonhosted.org/packages/09/97/fd421e8bc50766665ad35536c2bb4ef916533ba1fdd053a62d96cc7c8b95/numpy-2.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:56209416e81a7893036eea03abcb91c130643eb14233b2515c90dcac963fe99d", size = 12784672, upload-time = "2025-10-15T16:16:31.589Z" }, - { url = "https://files.pythonhosted.org/packages/ad/df/5474fb2f74970ca8eb978093969b125a84cc3d30e47f82191f981f13a8a0/numpy-2.3.4-cp313-cp313-win_arm64.whl", hash = "sha256:a700a4031bc0fd6936e78a752eefb79092cecad2599ea9c8039c548bc097f9bc", size = 10196702, upload-time = "2025-10-15T16:16:33.902Z" }, - { url = "https://files.pythonhosted.org/packages/11/83/66ac031464ec1767ea3ed48ce40f615eb441072945e98693bec0bcd056cc/numpy-2.3.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:86966db35c4040fdca64f0816a1c1dd8dbd027d90fca5a57e00e1ca4cd41b879", size = 21049003, upload-time = "2025-10-15T16:16:36.101Z" }, - { url = "https://files.pythonhosted.org/packages/5f/99/5b14e0e686e61371659a1d5bebd04596b1d72227ce36eed121bb0aeab798/numpy-2.3.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:838f045478638b26c375ee96ea89464d38428c69170360b23a1a50fa4baa3562", size = 14302980, upload-time = "2025-10-15T16:16:39.124Z" }, - { url = "https://files.pythonhosted.org/packages/2c/44/e9486649cd087d9fc6920e3fc3ac2aba10838d10804b1e179fb7cbc4e634/numpy-2.3.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d7315ed1dab0286adca467377c8381cd748f3dc92235f22a7dfc42745644a96a", size = 5231472, upload-time = "2025-10-15T16:16:41.168Z" }, - { url = "https://files.pythonhosted.org/packages/3e/51/902b24fa8887e5fe2063fd61b1895a476d0bbf46811ab0c7fdf4bd127345/numpy-2.3.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:84f01a4d18b2cc4ade1814a08e5f3c907b079c847051d720fad15ce37aa930b6", size = 6739342, upload-time = "2025-10-15T16:16:43.777Z" }, - { url = "https://files.pythonhosted.org/packages/34/f1/4de9586d05b1962acdcdb1dc4af6646361a643f8c864cef7c852bf509740/numpy-2.3.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:817e719a868f0dacde4abdfc5c1910b301877970195db9ab6a5e2c4bd5b121f7", size = 14354338, upload-time = "2025-10-15T16:16:46.081Z" }, - { url = "https://files.pythonhosted.org/packages/1f/06/1c16103b425de7969d5a76bdf5ada0804b476fed05d5f9e17b777f1cbefd/numpy-2.3.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85e071da78d92a214212cacea81c6da557cab307f2c34b5f85b628e94803f9c0", size = 16702392, upload-time = "2025-10-15T16:16:48.455Z" }, - { url = "https://files.pythonhosted.org/packages/34/b2/65f4dc1b89b5322093572b6e55161bb42e3e0487067af73627f795cc9d47/numpy-2.3.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2ec646892819370cf3558f518797f16597b4e4669894a2ba712caccc9da53f1f", size = 16134998, upload-time = "2025-10-15T16:16:51.114Z" }, - { url = "https://files.pythonhosted.org/packages/d4/11/94ec578896cdb973aaf56425d6c7f2aff4186a5c00fac15ff2ec46998b46/numpy-2.3.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:035796aaaddfe2f9664b9a9372f089cfc88bd795a67bd1bfe15e6e770934cf64", size = 18651574, upload-time = "2025-10-15T16:16:53.429Z" }, - { url = "https://files.pythonhosted.org/packages/62/b7/7efa763ab33dbccf56dade36938a77345ce8e8192d6b39e470ca25ff3cd0/numpy-2.3.4-cp313-cp313t-win32.whl", hash = "sha256:fea80f4f4cf83b54c3a051f2f727870ee51e22f0248d3114b8e755d160b38cfb", size = 6413135, upload-time = "2025-10-15T16:16:55.992Z" }, - { url = "https://files.pythonhosted.org/packages/43/70/aba4c38e8400abcc2f345e13d972fb36c26409b3e644366db7649015f291/numpy-2.3.4-cp313-cp313t-win_amd64.whl", hash = "sha256:15eea9f306b98e0be91eb344a94c0e630689ef302e10c2ce5f7e11905c704f9c", size = 12928582, upload-time = "2025-10-15T16:16:57.943Z" }, - { url = "https://files.pythonhosted.org/packages/67/63/871fad5f0073fc00fbbdd7232962ea1ac40eeaae2bba66c76214f7954236/numpy-2.3.4-cp313-cp313t-win_arm64.whl", hash = "sha256:b6c231c9c2fadbae4011ca5e7e83e12dc4a5072f1a1d85a0a7b3ed754d145a40", size = 10266691, upload-time = "2025-10-15T16:17:00.048Z" }, - { url = "https://files.pythonhosted.org/packages/72/71/ae6170143c115732470ae3a2d01512870dd16e0953f8a6dc89525696069b/numpy-2.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81c3e6d8c97295a7360d367f9f8553973651b76907988bb6066376bc2252f24e", size = 20955580, upload-time = "2025-10-15T16:17:02.509Z" }, - { url = "https://files.pythonhosted.org/packages/af/39/4be9222ffd6ca8a30eda033d5f753276a9c3426c397bb137d8e19dedd200/numpy-2.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7c26b0b2bf58009ed1f38a641f3db4be8d960a417ca96d14e5b06df1506d41ff", size = 14188056, upload-time = "2025-10-15T16:17:04.873Z" }, - { url = "https://files.pythonhosted.org/packages/6c/3d/d85f6700d0a4aa4f9491030e1021c2b2b7421b2b38d01acd16734a2bfdc7/numpy-2.3.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:62b2198c438058a20b6704351b35a1d7db881812d8512d67a69c9de1f18ca05f", size = 5116555, upload-time = "2025-10-15T16:17:07.499Z" }, - { url = "https://files.pythonhosted.org/packages/bf/04/82c1467d86f47eee8a19a464c92f90a9bb68ccf14a54c5224d7031241ffb/numpy-2.3.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:9d729d60f8d53a7361707f4b68a9663c968882dd4f09e0d58c044c8bf5faee7b", size = 6643581, upload-time = "2025-10-15T16:17:09.774Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d3/c79841741b837e293f48bd7db89d0ac7a4f2503b382b78a790ef1dc778a5/numpy-2.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd0c630cf256b0a7fd9d0a11c9413b42fef5101219ce6ed5a09624f5a65392c7", size = 14299186, upload-time = "2025-10-15T16:17:11.937Z" }, - { url = "https://files.pythonhosted.org/packages/e8/7e/4a14a769741fbf237eec5a12a2cbc7a4c4e061852b6533bcb9e9a796c908/numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5e081bc082825f8b139f9e9fe42942cb4054524598aaeb177ff476cc76d09d2", size = 16638601, upload-time = "2025-10-15T16:17:14.391Z" }, - { url = "https://files.pythonhosted.org/packages/93/87/1c1de269f002ff0a41173fe01dcc925f4ecff59264cd8f96cf3b60d12c9b/numpy-2.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15fb27364ed84114438fff8aaf998c9e19adbeba08c0b75409f8c452a8692c52", size = 16074219, upload-time = "2025-10-15T16:17:17.058Z" }, - { url = "https://files.pythonhosted.org/packages/cd/28/18f72ee77408e40a76d691001ae599e712ca2a47ddd2c4f695b16c65f077/numpy-2.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:85d9fb2d8cd998c84d13a79a09cc0c1091648e848e4e6249b0ccd7f6b487fa26", size = 18576702, upload-time = "2025-10-15T16:17:19.379Z" }, - { url = "https://files.pythonhosted.org/packages/c3/76/95650169b465ececa8cf4b2e8f6df255d4bf662775e797ade2025cc51ae6/numpy-2.3.4-cp314-cp314-win32.whl", hash = "sha256:e73d63fd04e3a9d6bc187f5455d81abfad05660b212c8804bf3b407e984cd2bc", size = 6337136, upload-time = "2025-10-15T16:17:22.886Z" }, - { url = "https://files.pythonhosted.org/packages/dc/89/a231a5c43ede5d6f77ba4a91e915a87dea4aeea76560ba4d2bf185c683f0/numpy-2.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:3da3491cee49cf16157e70f607c03a217ea6647b1cea4819c4f48e53d49139b9", size = 12920542, upload-time = "2025-10-15T16:17:24.783Z" }, - { url = "https://files.pythonhosted.org/packages/0d/0c/ae9434a888f717c5ed2ff2393b3f344f0ff6f1c793519fa0c540461dc530/numpy-2.3.4-cp314-cp314-win_arm64.whl", hash = "sha256:6d9cd732068e8288dbe2717177320723ccec4fb064123f0caf9bbd90ab5be868", size = 10480213, upload-time = "2025-10-15T16:17:26.935Z" }, - { url = "https://files.pythonhosted.org/packages/83/4b/c4a5f0841f92536f6b9592694a5b5f68c9ab37b775ff342649eadf9055d3/numpy-2.3.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:22758999b256b595cf0b1d102b133bb61866ba5ceecf15f759623b64c020c9ec", size = 21052280, upload-time = "2025-10-15T16:17:29.638Z" }, - { url = "https://files.pythonhosted.org/packages/3e/80/90308845fc93b984d2cc96d83e2324ce8ad1fd6efea81b324cba4b673854/numpy-2.3.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9cb177bc55b010b19798dc5497d540dea67fd13a8d9e882b2dae71de0cf09eb3", size = 14302930, upload-time = "2025-10-15T16:17:32.384Z" }, - { url = "https://files.pythonhosted.org/packages/3d/4e/07439f22f2a3b247cec4d63a713faae55e1141a36e77fb212881f7cda3fb/numpy-2.3.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0f2bcc76f1e05e5ab58893407c63d90b2029908fa41f9f1cc51eecce936c3365", size = 5231504, upload-time = "2025-10-15T16:17:34.515Z" }, - { url = "https://files.pythonhosted.org/packages/ab/de/1e11f2547e2fe3d00482b19721855348b94ada8359aef5d40dd57bfae9df/numpy-2.3.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dc20bde86802df2ed8397a08d793da0ad7a5fd4ea3ac85d757bf5dd4ad7c252", size = 6739405, upload-time = "2025-10-15T16:17:36.128Z" }, - { url = "https://files.pythonhosted.org/packages/3b/40/8cd57393a26cebe2e923005db5134a946c62fa56a1087dc7c478f3e30837/numpy-2.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e199c087e2aa71c8f9ce1cb7a8e10677dc12457e7cc1be4798632da37c3e86e", size = 14354866, upload-time = "2025-10-15T16:17:38.884Z" }, - { url = "https://files.pythonhosted.org/packages/93/39/5b3510f023f96874ee6fea2e40dfa99313a00bf3ab779f3c92978f34aace/numpy-2.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85597b2d25ddf655495e2363fe044b0ae999b75bc4d630dc0d886484b03a5eb0", size = 16703296, upload-time = "2025-10-15T16:17:41.564Z" }, - { url = "https://files.pythonhosted.org/packages/41/0d/19bb163617c8045209c1996c4e427bccbc4bbff1e2c711f39203c8ddbb4a/numpy-2.3.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04a69abe45b49c5955923cf2c407843d1c85013b424ae8a560bba16c92fe44a0", size = 16136046, upload-time = "2025-10-15T16:17:43.901Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c1/6dba12fdf68b02a21ac411c9df19afa66bed2540f467150ca64d246b463d/numpy-2.3.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e1708fac43ef8b419c975926ce1eaf793b0c13b7356cfab6ab0dc34c0a02ac0f", size = 18652691, upload-time = "2025-10-15T16:17:46.247Z" }, - { url = "https://files.pythonhosted.org/packages/f8/73/f85056701dbbbb910c51d846c58d29fd46b30eecd2b6ba760fc8b8a1641b/numpy-2.3.4-cp314-cp314t-win32.whl", hash = "sha256:863e3b5f4d9915aaf1b8ec79ae560ad21f0b8d5e3adc31e73126491bb86dee1d", size = 6485782, upload-time = "2025-10-15T16:17:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/17/90/28fa6f9865181cb817c2471ee65678afa8a7e2a1fb16141473d5fa6bacc3/numpy-2.3.4-cp314-cp314t-win_amd64.whl", hash = "sha256:962064de37b9aef801d33bc579690f8bfe6c5e70e29b61783f60bcba838a14d6", size = 13113301, upload-time = "2025-10-15T16:17:50.938Z" }, - { url = "https://files.pythonhosted.org/packages/54/23/08c002201a8e7e1f9afba93b97deceb813252d9cfd0d3351caed123dcf97/numpy-2.3.4-cp314-cp314t-win_arm64.whl", hash = "sha256:8b5a9a39c45d852b62693d9b3f3e0fe052541f804296ff401a72a1b60edafb29", size = 10547532, upload-time = "2025-10-15T16:17:53.48Z" }, +version = "2.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/65/21b3bc86aac7b8f2862db1e808f1ea22b028e30a225a34a5ede9bf8678f2/numpy-2.3.5.tar.gz", hash = "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", size = 20584950, upload-time = "2025-11-16T22:52:42.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/69/9cde09f36da4b5a505341180a3f2e6fadc352fd4d2b7096ce9778db83f1a/numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff", size = 16728251, upload-time = "2025-11-16T22:50:19.013Z" }, + { url = "https://files.pythonhosted.org/packages/79/fb/f505c95ceddd7027347b067689db71ca80bd5ecc926f913f1a23e65cf09b/numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188", size = 12254652, upload-time = "2025-11-16T22:50:21.487Z" }, + { url = "https://files.pythonhosted.org/packages/78/da/8c7738060ca9c31b30e9301ee0cf6c5ffdbf889d9593285a1cead337f9a5/numpy-2.3.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0", size = 5083172, upload-time = "2025-11-16T22:50:24.562Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b4/ee5bb2537fb9430fd2ef30a616c3672b991a4129bb1c7dcc42aa0abbe5d7/numpy-2.3.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903", size = 6622990, upload-time = "2025-11-16T22:50:26.47Z" }, + { url = "https://files.pythonhosted.org/packages/95/03/dc0723a013c7d7c19de5ef29e932c3081df1c14ba582b8b86b5de9db7f0f/numpy-2.3.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d", size = 14248902, upload-time = "2025-11-16T22:50:28.861Z" }, + { url = "https://files.pythonhosted.org/packages/f5/10/ca162f45a102738958dcec8023062dad0cbc17d1ab99d68c4e4a6c45fb2b/numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017", size = 16597430, upload-time = "2025-11-16T22:50:31.56Z" }, + { url = "https://files.pythonhosted.org/packages/2a/51/c1e29be863588db58175175f057286900b4b3327a1351e706d5e0f8dd679/numpy-2.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf", size = 16024551, upload-time = "2025-11-16T22:50:34.242Z" }, + { url = "https://files.pythonhosted.org/packages/83/68/8236589d4dbb87253d28259d04d9b814ec0ecce7cb1c7fed29729f4c3a78/numpy-2.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce", size = 18533275, upload-time = "2025-11-16T22:50:37.651Z" }, + { url = "https://files.pythonhosted.org/packages/40/56/2932d75b6f13465239e3b7b7e511be27f1b8161ca2510854f0b6e521c395/numpy-2.3.5-cp313-cp313-win32.whl", hash = "sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e", size = 6277637, upload-time = "2025-11-16T22:50:40.11Z" }, + { url = "https://files.pythonhosted.org/packages/0c/88/e2eaa6cffb115b85ed7c7c87775cb8bcf0816816bc98ca8dbfa2ee33fe6e/numpy-2.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b", size = 12779090, upload-time = "2025-11-16T22:50:42.503Z" }, + { url = "https://files.pythonhosted.org/packages/8f/88/3f41e13a44ebd4034ee17baa384acac29ba6a4fcc2aca95f6f08ca0447d1/numpy-2.3.5-cp313-cp313-win_arm64.whl", hash = "sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae", size = 10194710, upload-time = "2025-11-16T22:50:44.971Z" }, + { url = "https://files.pythonhosted.org/packages/13/cb/71744144e13389d577f867f745b7df2d8489463654a918eea2eeb166dfc9/numpy-2.3.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd", size = 16827292, upload-time = "2025-11-16T22:50:47.715Z" }, + { url = "https://files.pythonhosted.org/packages/71/80/ba9dc6f2a4398e7f42b708a7fdc841bb638d353be255655498edbf9a15a8/numpy-2.3.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f", size = 12378897, upload-time = "2025-11-16T22:50:51.327Z" }, + { url = "https://files.pythonhosted.org/packages/2e/6d/db2151b9f64264bcceccd51741aa39b50150de9b602d98ecfe7e0c4bff39/numpy-2.3.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a", size = 5207391, upload-time = "2025-11-16T22:50:54.542Z" }, + { url = "https://files.pythonhosted.org/packages/80/ae/429bacace5ccad48a14c4ae5332f6aa8ab9f69524193511d60ccdfdc65fa/numpy-2.3.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139", size = 6721275, upload-time = "2025-11-16T22:50:56.794Z" }, + { url = "https://files.pythonhosted.org/packages/74/5b/1919abf32d8722646a38cd527bc3771eb229a32724ee6ba340ead9b92249/numpy-2.3.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e", size = 14306855, upload-time = "2025-11-16T22:50:59.208Z" }, + { url = "https://files.pythonhosted.org/packages/a5/87/6831980559434973bebc30cd9c1f21e541a0f2b0c280d43d3afd909b66d0/numpy-2.3.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9", size = 16657359, upload-time = "2025-11-16T22:51:01.991Z" }, + { url = "https://files.pythonhosted.org/packages/dd/91/c797f544491ee99fd00495f12ebb7802c440c1915811d72ac5b4479a3356/numpy-2.3.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946", size = 16093374, upload-time = "2025-11-16T22:51:05.291Z" }, + { url = "https://files.pythonhosted.org/packages/74/a6/54da03253afcbe7a72785ec4da9c69fb7a17710141ff9ac5fcb2e32dbe64/numpy-2.3.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1", size = 18594587, upload-time = "2025-11-16T22:51:08.585Z" }, + { url = "https://files.pythonhosted.org/packages/80/e9/aff53abbdd41b0ecca94285f325aff42357c6b5abc482a3fcb4994290b18/numpy-2.3.5-cp313-cp313t-win32.whl", hash = "sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3", size = 6405940, upload-time = "2025-11-16T22:51:11.541Z" }, + { url = "https://files.pythonhosted.org/packages/d5/81/50613fec9d4de5480de18d4f8ef59ad7e344d497edbef3cfd80f24f98461/numpy-2.3.5-cp313-cp313t-win_amd64.whl", hash = "sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234", size = 12920341, upload-time = "2025-11-16T22:51:14.312Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ab/08fd63b9a74303947f34f0bd7c5903b9c5532c2d287bead5bdf4c556c486/numpy-2.3.5-cp313-cp313t-win_arm64.whl", hash = "sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7", size = 10262507, upload-time = "2025-11-16T22:51:16.846Z" }, + { url = "https://files.pythonhosted.org/packages/ba/97/1a914559c19e32d6b2e233cf9a6a114e67c856d35b1d6babca571a3e880f/numpy-2.3.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82", size = 16735706, upload-time = "2025-11-16T22:51:19.558Z" }, + { url = "https://files.pythonhosted.org/packages/57/d4/51233b1c1b13ecd796311216ae417796b88b0616cfd8a33ae4536330748a/numpy-2.3.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0", size = 12264507, upload-time = "2025-11-16T22:51:22.492Z" }, + { url = "https://files.pythonhosted.org/packages/45/98/2fe46c5c2675b8306d0b4a3ec3494273e93e1226a490f766e84298576956/numpy-2.3.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63", size = 5093049, upload-time = "2025-11-16T22:51:25.171Z" }, + { url = "https://files.pythonhosted.org/packages/ce/0e/0698378989bb0ac5f1660c81c78ab1fe5476c1a521ca9ee9d0710ce54099/numpy-2.3.5-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9", size = 6626603, upload-time = "2025-11-16T22:51:27Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a6/9ca0eecc489640615642a6cbc0ca9e10df70df38c4d43f5a928ff18d8827/numpy-2.3.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b", size = 14262696, upload-time = "2025-11-16T22:51:29.402Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f6/07ec185b90ec9d7217a00eeeed7383b73d7e709dae2a9a021b051542a708/numpy-2.3.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520", size = 16597350, upload-time = "2025-11-16T22:51:32.167Z" }, + { url = "https://files.pythonhosted.org/packages/75/37/164071d1dde6a1a84c9b8e5b414fa127981bad47adf3a6b7e23917e52190/numpy-2.3.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c", size = 16040190, upload-time = "2025-11-16T22:51:35.403Z" }, + { url = "https://files.pythonhosted.org/packages/08/3c/f18b82a406b04859eb026d204e4e1773eb41c5be58410f41ffa511d114ae/numpy-2.3.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8", size = 18536749, upload-time = "2025-11-16T22:51:39.698Z" }, + { url = "https://files.pythonhosted.org/packages/40/79/f82f572bf44cf0023a2fe8588768e23e1592585020d638999f15158609e1/numpy-2.3.5-cp314-cp314-win32.whl", hash = "sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248", size = 6335432, upload-time = "2025-11-16T22:51:42.476Z" }, + { url = "https://files.pythonhosted.org/packages/a3/2e/235b4d96619931192c91660805e5e49242389742a7a82c27665021db690c/numpy-2.3.5-cp314-cp314-win_amd64.whl", hash = "sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e", size = 12919388, upload-time = "2025-11-16T22:51:45.275Z" }, + { url = "https://files.pythonhosted.org/packages/07/2b/29fd75ce45d22a39c61aad74f3d718e7ab67ccf839ca8b60866054eb15f8/numpy-2.3.5-cp314-cp314-win_arm64.whl", hash = "sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2", size = 10476651, upload-time = "2025-11-16T22:51:47.749Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/f6a721234ebd4d87084cfa68d081bcba2f5cfe1974f7de4e0e8b9b2a2ba1/numpy-2.3.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41", size = 16834503, upload-time = "2025-11-16T22:51:50.443Z" }, + { url = "https://files.pythonhosted.org/packages/5c/1c/baf7ffdc3af9c356e1c135e57ab7cf8d247931b9554f55c467efe2c69eff/numpy-2.3.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad", size = 12381612, upload-time = "2025-11-16T22:51:53.609Z" }, + { url = "https://files.pythonhosted.org/packages/74/91/f7f0295151407ddc9ba34e699013c32c3c91944f9b35fcf9281163dc1468/numpy-2.3.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39", size = 5210042, upload-time = "2025-11-16T22:51:56.213Z" }, + { url = "https://files.pythonhosted.org/packages/2e/3b/78aebf345104ec50dd50a4d06ddeb46a9ff5261c33bcc58b1c4f12f85ec2/numpy-2.3.5-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20", size = 6724502, upload-time = "2025-11-16T22:51:58.584Z" }, + { url = "https://files.pythonhosted.org/packages/02/c6/7c34b528740512e57ef1b7c8337ab0b4f0bddf34c723b8996c675bc2bc91/numpy-2.3.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52", size = 14308962, upload-time = "2025-11-16T22:52:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/80/35/09d433c5262bc32d725bafc619e095b6a6651caf94027a03da624146f655/numpy-2.3.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b", size = 16655054, upload-time = "2025-11-16T22:52:04.267Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ab/6a7b259703c09a88804fa2430b43d6457b692378f6b74b356155283566ac/numpy-2.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3", size = 16091613, upload-time = "2025-11-16T22:52:08.651Z" }, + { url = "https://files.pythonhosted.org/packages/c2/88/330da2071e8771e60d1038166ff9d73f29da37b01ec3eb43cb1427464e10/numpy-2.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227", size = 18591147, upload-time = "2025-11-16T22:52:11.453Z" }, + { url = "https://files.pythonhosted.org/packages/51/41/851c4b4082402d9ea860c3626db5d5df47164a712cb23b54be028b184c1c/numpy-2.3.5-cp314-cp314t-win32.whl", hash = "sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5", size = 6479806, upload-time = "2025-11-16T22:52:14.641Z" }, + { url = "https://files.pythonhosted.org/packages/90/30/d48bde1dfd93332fa557cff1972fbc039e055a52021fbef4c2c4b1eefd17/numpy-2.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf", size = 13105760, upload-time = "2025-11-16T22:52:17.975Z" }, + { url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459, upload-time = "2025-11-16T22:52:20.55Z" }, ] [[package]] @@ -1241,16 +1311,16 @@ wheels = [ [[package]] name = "paracelsus" -version = "0.12.0" +version = "0.13.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydot" }, { name = "sqlalchemy" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/fe/84f4d06ac3e6038384847dc1d5c8b956f61b780f69509d177107b550c7b9/paracelsus-0.12.0.tar.gz", hash = "sha256:f1d8f584ebc445db99a2906f97ff55f36ae663c104320dd4a6b5b78b4fa24dce", size = 83664, upload-time = "2025-10-07T12:45:41.112Z" } +sdist = { url = "https://files.pythonhosted.org/packages/03/ca/adad895f85086a2942d4a47b02d0df02d99db3bd6adc904c796c487eb110/paracelsus-0.13.2.tar.gz", hash = "sha256:1865e68a6cd56e8c1a003266abfe2d4f7d8ec187c8649098d12c2ba8d4f8b48a", size = 86625, upload-time = "2025-11-22T01:36:30.514Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/60/9062e4072c16750b6b01bac9c55b329b249ee7c970d61c128049be197d7a/paracelsus-0.12.0-py3-none-any.whl", hash = "sha256:01f5a508174d06a86d53374215a0c85962498361ac3f0bd3450023760d3b3836", size = 81236, upload-time = "2025-10-07T12:45:39.929Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0e/7a7ba5b69bd6e75533e5ae046e359a74008366139d8fd36fcbfe3ac5923a/paracelsus-0.13.2-py3-none-any.whl", hash = "sha256:330782a682225f2ece59e29d7cc93ab902d177889d88eb2a97efa09a2fd9cc45", size = 15837, upload-time = "2025-11-22T01:36:29.12Z" }, ] [[package]] @@ -1334,29 +1404,29 @@ wheels = [ [[package]] name = "protobuf" -version = "6.33.0" +version = "6.33.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/ff/64a6c8f420818bb873713988ca5492cba3a7946be57e027ac63495157d97/protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954", size = 443463, upload-time = "2025-10-15T20:39:52.159Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/03/a1440979a3f74f16cab3b75b0da1a1a7f922d56a8ddea96092391998edc0/protobuf-6.33.1.tar.gz", hash = "sha256:97f65757e8d09870de6fd973aeddb92f85435607235d20b2dfed93405d00c85b", size = 443432, upload-time = "2025-11-13T16:44:18.895Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/ee/52b3fa8feb6db4a833dfea4943e175ce645144532e8a90f72571ad85df4e/protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035", size = 425593, upload-time = "2025-10-15T20:39:40.29Z" }, - { url = "https://files.pythonhosted.org/packages/7b/c6/7a465f1825872c55e0341ff4a80198743f73b69ce5d43ab18043699d1d81/protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee", size = 436882, upload-time = "2025-10-15T20:39:42.841Z" }, - { url = "https://files.pythonhosted.org/packages/e1/a9/b6eee662a6951b9c3640e8e452ab3e09f117d99fc10baa32d1581a0d4099/protobuf-6.33.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455", size = 427521, upload-time = "2025-10-15T20:39:43.803Z" }, - { url = "https://files.pythonhosted.org/packages/10/35/16d31e0f92c6d2f0e77c2a3ba93185130ea13053dd16200a57434c882f2b/protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90", size = 324445, upload-time = "2025-10-15T20:39:44.932Z" }, - { url = "https://files.pythonhosted.org/packages/e6/eb/2a981a13e35cda8b75b5585aaffae2eb904f8f351bdd3870769692acbd8a/protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298", size = 339159, upload-time = "2025-10-15T20:39:46.186Z" }, - { url = "https://files.pythonhosted.org/packages/21/51/0b1cbad62074439b867b4e04cc09b93f6699d78fd191bed2bbb44562e077/protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef", size = 323172, upload-time = "2025-10-15T20:39:47.465Z" }, - { url = "https://files.pythonhosted.org/packages/07/d1/0a28c21707807c6aacd5dc9c3704b2aa1effbf37adebd8caeaf68b17a636/protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995", size = 170477, upload-time = "2025-10-15T20:39:51.311Z" }, + { url = "https://files.pythonhosted.org/packages/06/f1/446a9bbd2c60772ca36556bac8bfde40eceb28d9cc7838755bc41e001d8f/protobuf-6.33.1-cp310-abi3-win32.whl", hash = "sha256:f8d3fdbc966aaab1d05046d0240dd94d40f2a8c62856d41eaa141ff64a79de6b", size = 425593, upload-time = "2025-11-13T16:44:06.275Z" }, + { url = "https://files.pythonhosted.org/packages/a6/79/8780a378c650e3df849b73de8b13cf5412f521ca2ff9b78a45c247029440/protobuf-6.33.1-cp310-abi3-win_amd64.whl", hash = "sha256:923aa6d27a92bf44394f6abf7ea0500f38769d4b07f4be41cb52bd8b1123b9ed", size = 436883, upload-time = "2025-11-13T16:44:09.222Z" }, + { url = "https://files.pythonhosted.org/packages/cd/93/26213ff72b103ae55bb0d73e7fb91ea570ef407c3ab4fd2f1f27cac16044/protobuf-6.33.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:fe34575f2bdde76ac429ec7b570235bf0c788883e70aee90068e9981806f2490", size = 427522, upload-time = "2025-11-13T16:44:10.475Z" }, + { url = "https://files.pythonhosted.org/packages/c2/32/df4a35247923393aa6b887c3b3244a8c941c32a25681775f96e2b418f90e/protobuf-6.33.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:f8adba2e44cde2d7618996b3fc02341f03f5bc3f2748be72dc7b063319276178", size = 324445, upload-time = "2025-11-13T16:44:11.869Z" }, + { url = "https://files.pythonhosted.org/packages/8e/d0/d796e419e2ec93d2f3fa44888861c3f88f722cde02b7c3488fcc6a166820/protobuf-6.33.1-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:0f4cf01222c0d959c2b399142deb526de420be8236f22c71356e2a544e153c53", size = 339161, upload-time = "2025-11-13T16:44:12.778Z" }, + { url = "https://files.pythonhosted.org/packages/1d/2a/3c5f05a4af06649547027d288747f68525755de692a26a7720dced3652c0/protobuf-6.33.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:8fd7d5e0eb08cd5b87fd3df49bc193f5cfd778701f47e11d127d0afc6c39f1d1", size = 323171, upload-time = "2025-11-13T16:44:14.035Z" }, + { url = "https://files.pythonhosted.org/packages/08/b4/46310463b4f6ceef310f8348786f3cff181cea671578e3d9743ba61a459e/protobuf-6.33.1-py3-none-any.whl", hash = "sha256:d595a9fd694fdeb061a62fbe10eb039cc1e444df81ec9bb70c7fc59ebcb1eafa", size = 170477, upload-time = "2025-11-13T16:44:17.633Z" }, ] [[package]] name = "psycopg" -version = "3.2.12" +version = "3.2.13" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/77/c72d10262b872617e509a0c60445afcc4ce2cd5cd6bc1c97700246d69c85/psycopg-3.2.12.tar.gz", hash = "sha256:85c08d6f6e2a897b16280e0ff6406bef29b1327c045db06d21f364d7cd5da90b", size = 160642, upload-time = "2025-10-26T00:46:03.045Z" } +sdist = { url = "https://files.pythonhosted.org/packages/44/05/d4a05988f15fcf90e0088c735b1f2fc04a30b7fc65461d6ec278f5f2f17a/psycopg-3.2.13.tar.gz", hash = "sha256:309adaeda61d44556046ec9a83a93f42bbe5310120b1995f3af49ab6d9f13c1d", size = 160626, upload-time = "2025-11-21T22:34:32.328Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/28/8c4f90e415411dc9c78d6ba10b549baa324659907c13f64bfe3779d4066c/psycopg-3.2.12-py3-none-any.whl", hash = "sha256:8a1611a2d4c16ae37eada46438be9029a35bb959bb50b3d0e1e93c0f3d54c9ee", size = 206765, upload-time = "2025-10-26T00:10:42.173Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/f2724bd1986158a348316e86fdd0837a838b14a711df3f00e47fba597447/psycopg-3.2.13-py3-none-any.whl", hash = "sha256:a481374514f2da627157f767a9336705ebefe93ea7a0522a6cbacba165da179a", size = 206797, upload-time = "2025-11-21T22:29:39.733Z" }, ] [package.optional-dependencies] @@ -1366,27 +1436,27 @@ binary = [ [[package]] name = "psycopg-binary" -version = "3.2.12" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/0b/9d480aba4a4864832c29e6fc94ddd34d9927c276448eb3b56ffe24ed064c/psycopg_binary-3.2.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:442f20153415f374ae5753ca618637611a41a3c58c56d16ce55f845d76a3cf7b", size = 4017829, upload-time = "2025-10-26T00:26:27.031Z" }, - { url = "https://files.pythonhosted.org/packages/a4/f3/0d294b30349bde24a46741a1f27a10e8ab81e9f4118d27c2fe592acfb42a/psycopg_binary-3.2.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:79de3cc5adbf51677009a8fda35ac9e9e3686d5595ab4b0c43ec7099ece6aeb5", size = 4089835, upload-time = "2025-10-26T00:27:01.392Z" }, - { url = "https://files.pythonhosted.org/packages/82/d4/ff82e318e5a55d6951b278d3af7b4c7c1b19344e3a3722b6613f156a38ea/psycopg_binary-3.2.12-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:095ccda59042a1239ac2fefe693a336cb5cecf8944a8d9e98b07f07e94e2b78d", size = 4625474, upload-time = "2025-10-26T00:27:40.34Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e8/2c9df6475a5ab6d614d516f4497c568d84f7d6c21d0e11444468c9786c9f/psycopg_binary-3.2.12-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:efab679a2c7d1bf7d0ec0e1ecb47fe764945eff75bb4321f2e699b30a12db9b3", size = 4720350, upload-time = "2025-10-26T00:28:20.104Z" }, - { url = "https://files.pythonhosted.org/packages/74/f5/7aec81b0c41985dc006e2d5822486ad4b7c2a1a97a5a05e37dc2adaf1512/psycopg_binary-3.2.12-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d369e79ad9647fc8217cbb51bbbf11f9a1ffca450be31d005340157ffe8e91b3", size = 4411621, upload-time = "2025-10-26T00:28:59.104Z" }, - { url = "https://files.pythonhosted.org/packages/fc/15/d3cb41b8fa9d5f14320ab250545fbb66f9ddb481e448e618902672a806c0/psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eedc410f82007038030650aa58f620f9fe0009b9d6b04c3dc71cbd3bae5b2675", size = 3863081, upload-time = "2025-10-26T00:29:31.235Z" }, - { url = "https://files.pythonhosted.org/packages/69/8a/72837664e63e3cd3aa145cedcf29e5c21257579739aba78ab7eb668f7d9c/psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f3bae4be7f6781bf6c9576eedcd5e1bb74468126fa6de991e47cdb1a8ea3a42a", size = 3537428, upload-time = "2025-10-26T00:30:01.465Z" }, - { url = "https://files.pythonhosted.org/packages/cc/7e/1b78ae38e7d69e6d7fb1e2dcce101493f5fa429480bac3a68b876c9b1635/psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8ffe75fe6be902dadd439adf4228c98138a992088e073ede6dd34e7235f4e03e", size = 3585981, upload-time = "2025-10-26T00:30:31.635Z" }, - { url = "https://files.pythonhosted.org/packages/a3/f8/245b4868b2dac46c3fb6383b425754ae55df1910c826d305ed414da03777/psycopg_binary-3.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:2598d0e4f2f258da13df0560187b3f1dfc9b8688c46b9d90176360ae5212c3fc", size = 2912929, upload-time = "2025-10-26T00:30:56.413Z" }, - { url = "https://files.pythonhosted.org/packages/5c/5b/76fbb40b981b73b285a00dccafc38cf67b7a9b3f7d4f2025dda7b896e7ef/psycopg_binary-3.2.12-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dc68094e00a5a7e8c20de1d3a0d5e404a27f522e18f8eb62bbbc9f865c3c81ef", size = 4016868, upload-time = "2025-10-26T00:31:29.974Z" }, - { url = "https://files.pythonhosted.org/packages/0e/08/8841ae3e2d1a3228e79eaaf5b7f991d15f0a231bb5031a114305b19724b1/psycopg_binary-3.2.12-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2d55009eeddbef54c711093c986daaf361d2c4210aaa1ee905075a3b97a62441", size = 4090508, upload-time = "2025-10-26T00:32:04.192Z" }, - { url = "https://files.pythonhosted.org/packages/05/de/a41f62230cf4095ae4547eceada218cf28c17e7f94376913c1c8dde9546f/psycopg_binary-3.2.12-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:66a031f22e4418016990446d3e38143826f03ad811b9f78f58e2afbc1d343f7a", size = 4629788, upload-time = "2025-10-26T00:32:43.28Z" }, - { url = "https://files.pythonhosted.org/packages/45/19/529d92134eae44475f781a86d58cdf3edd0953e17c69762abf387a9f2636/psycopg_binary-3.2.12-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:58ed30d33c25d7dc8d2f06285e88493147c2a660cc94713e4b563a99efb80a1f", size = 4724124, upload-time = "2025-10-26T00:33:22.594Z" }, - { url = "https://files.pythonhosted.org/packages/5c/f5/97344e87065f7c9713ce213a2cff7732936ec3af6622e4b2a88715a953f2/psycopg_binary-3.2.12-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e0b5ccd03ca4749b8f66f38608ccbcb415cbd130d02de5eda80d042b83bee90e", size = 4411340, upload-time = "2025-10-26T00:34:00.759Z" }, - { url = "https://files.pythonhosted.org/packages/b1/c2/34bce068f6bfb4c2e7bb1187bb64a3f3be254702b158c4ad05eacc0055cf/psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:909de94de7dd4d6086098a5755562207114c9638ec42c52d84c8a440c45fe084", size = 3867815, upload-time = "2025-10-26T00:34:33.181Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a1/c647e01ab162e6bfa52380e23e486215e9d28ffd31e9cf3cb1e9ca59008b/psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:7130effd0517881f3a852eff98729d51034128f0737f64f0d1c7ea8343d77bd7", size = 3541756, upload-time = "2025-10-26T00:35:08.622Z" }, - { url = "https://files.pythonhosted.org/packages/6b/d0/795bdaa8c946a7b7126bf7ca8d4371eaaa613093e3ec341a0e50f52cbee2/psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:89b3c5201ca616d69ca0c3c0003ca18f7170a679c445c7e386ebfb4f29aa738e", size = 3587950, upload-time = "2025-10-26T00:35:41.183Z" }, - { url = "https://files.pythonhosted.org/packages/53/cf/10c3e95827a3ca8af332dfc471befec86e15a14dc83cee893c49a4910dad/psycopg_binary-3.2.12-cp314-cp314-win_amd64.whl", hash = "sha256:48a8e29f3e38fcf8d393b8fe460d83e39c107ad7e5e61cd3858a7569e0554a39", size = 3005787, upload-time = "2025-10-26T00:36:06.783Z" }, +version = "3.2.13" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/ec/ef37bb44dc02fcc6c0a3eeb93f4baaac13bcb228633fe38ad3fb5a3f6449/psycopg_binary-3.2.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dbae6ab1966e2b61d97e47220556c330c4608bb4cfb3a124aa0595c39995c068", size = 3995628, upload-time = "2025-11-21T22:31:45.921Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ad/4748f5f1a40248af16dba087dbec50bd335ee025cc1fb9bf64773378ceff/psycopg_binary-3.2.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fae933e4564386199fc54845d85413eedb49760e0bcd2b621fde2dd1825b99b3", size = 4069024, upload-time = "2025-11-21T22:31:50.202Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c2/f02ec6bbc30c7fcd3b39823d2d624b42fae480edeb6e50eb3276281d5635/psycopg_binary-3.2.13-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:13e2f8894d410678529ff9f1211f96c5a93ff142f992b302682b42d924428b61", size = 4615127, upload-time = "2025-11-21T22:31:56.517Z" }, + { url = "https://files.pythonhosted.org/packages/f0/0d/a54fc2cdd672c84175d6869cc823d6ec2a8909318d491f3c24e6077983f2/psycopg_binary-3.2.13-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f26f7009375cf1e92180e5c517c52da1054f7e690dde90e0ed00fa8b5736bcd4", size = 4710267, upload-time = "2025-11-21T22:32:04.585Z" }, + { url = "https://files.pythonhosted.org/packages/9d/b7/067de1acaf3d312253351f3af4121f972584bd36cada6378d4b0cdcebd38/psycopg_binary-3.2.13-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea2fdbcc9142933a47c66970e0df8b363e3bd1ea4c5ce376f2f3d94a9aeec847", size = 4400795, upload-time = "2025-11-21T22:32:08.883Z" }, + { url = "https://files.pythonhosted.org/packages/64/b5/030e6b1ebfc4d3a8fca03adc5fc827982643bad0b01a1268538d17c08ed3/psycopg_binary-3.2.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac92d6bc1d4a41c7459953a9aa727b9966e937e94c9e072527317fd2a67d488b", size = 3851239, upload-time = "2025-11-21T22:32:12.333Z" }, + { url = "https://files.pythonhosted.org/packages/79/6f/0541845364a7de9eae6807060da6a04b22a8eb2e803606d285d9250fbe93/psycopg_binary-3.2.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8b843c00478739e95c46d6d3472b13123b634685f107831a9bfc41503a06ecbd", size = 3525084, upload-time = "2025-11-21T22:32:15.946Z" }, + { url = "https://files.pythonhosted.org/packages/83/ae/6507890dc30a4bbd9d938d4ff3a4079d009a5ad8170af51c7f762438fdbf/psycopg_binary-3.2.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2f63868cc96bc18486cebec24445affbdd7f7debf28fac466ea935a8b5a4753b", size = 3576787, upload-time = "2025-11-21T22:32:19.922Z" }, + { url = "https://files.pythonhosted.org/packages/9d/64/3d1c2f1fd09b60cdfbe68b9a810b357ba505eff6e4bdb1a2d9f6729da64c/psycopg_binary-3.2.13-cp313-cp313-win_amd64.whl", hash = "sha256:594dfbca3326e997ae738d3d339004e8416b1f7390f52ce8dc2d692393e8fa96", size = 2905584, upload-time = "2025-11-21T22:32:23.399Z" }, + { url = "https://files.pythonhosted.org/packages/d3/b4/7656b3d67bedff2b900c8c4671cb6eb5fb99c2fc36da33579cac89779c25/psycopg_binary-3.2.13-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:502a778c3e07c6b3aabfa56ee230e8c264d2debfab42d11535513a01bdfff0d6", size = 3997201, upload-time = "2025-11-21T22:32:28.185Z" }, + { url = "https://files.pythonhosted.org/packages/e0/2e/3b4afbd94d48df19c3931cedba464b109f89d81ac43178e6a3d654b4e8d5/psycopg_binary-3.2.13-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7561a71d764d6f74d66e8b7d844b0f27fa33de508f65c17b1d56a94c73644776", size = 4071631, upload-time = "2025-11-21T22:32:32.594Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8b/107d06d55992e2f13157eb705ba5a47d06c4cf1bed077dff0c567b10c187/psycopg_binary-3.2.13-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9caf14745a1930b4e03fe4072cd7154eaf6e1241d20c42130ed784408a26b24b", size = 4620918, upload-time = "2025-11-21T22:32:37.357Z" }, + { url = "https://files.pythonhosted.org/packages/e1/47/a925620f261b115f31e813a5bfe640f316413b1864094a60162f4a6e4d67/psycopg_binary-3.2.13-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a6cafabdc0bfa37e11c6f365020fd5916b62d6296df581f4dceaa43a2ce680c", size = 4714494, upload-time = "2025-11-21T22:32:42.138Z" }, + { url = "https://files.pythonhosted.org/packages/46/33/bed384665356bb9ba17dd8e104884d87cc2343d16dffdfd9aaa9a159bd4d/psycopg_binary-3.2.13-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96cb5a27e68acac6d74b64fca38592a692de9c4b7827339190698d58027aa45", size = 4403046, upload-time = "2025-11-21T22:32:47.241Z" }, + { url = "https://files.pythonhosted.org/packages/41/88/749d8e8102fb5df502e2ecb053b79e78e3358af01af652b5dbeb96ab7905/psycopg_binary-3.2.13-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:596176ae3dfbf56fc61108870bfe17c7205d33ac28d524909feb5335201daa0a", size = 3859046, upload-time = "2025-11-21T22:32:51.481Z" }, + { url = "https://files.pythonhosted.org/packages/38/7c/f492e63b517d6dcd564e8c43bc15e11a4c712a848adf8938ce33bfd4c867/psycopg_binary-3.2.13-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:cc3a0408435dfbb77eeca5e8050df4b19a6e9b7e5e5583edf524c4a83d6293b2", size = 3531351, upload-time = "2025-11-21T22:32:55.571Z" }, + { url = "https://files.pythonhosted.org/packages/07/5a/d8743eb23944e5cf2a0bbfa92935c140b5beaacdb872be641065ed70ab2c/psycopg_binary-3.2.13-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:65df0d459ffba14082d8ca4bb2f6ffbb2f8d02968f7d34a747e1031934b76b23", size = 3581034, upload-time = "2025-11-21T22:33:01.648Z" }, + { url = "https://files.pythonhosted.org/packages/46/b2/411d4180252144f7eff024894d2d2ebb98c012c944a282fc20250870e461/psycopg_binary-3.2.13-cp314-cp314-win_amd64.whl", hash = "sha256:5c77f156c7316529ed371b5f95a51139e531328ee39c37493a2afcbc1f79d5de", size = 3000162, upload-time = "2025-11-21T22:33:07.378Z" }, ] [[package]] @@ -1524,16 +1594,16 @@ wheels = [ [[package]] name = "pydantic-settings" -version = "2.11.0" +version = "2.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/c5/dbbc27b814c71676593d1c3f718e6cd7d4f00652cefa24b75f7aa3efb25e/pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180", size = 188394, upload-time = "2025-09-24T14:19:11.764Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/d6/887a1ff844e64aa823fb4905978d882a633cfe295c32eacad582b78a7d8b/pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c", size = 48608, upload-time = "2025-09-24T14:19:10.015Z" }, + { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, ] [[package]] @@ -1604,7 +1674,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.4.2" +version = "9.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1613,9 +1683,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/56/f013048ac4bc4c1d9be45afd4ab209ea62822fb1598f40687e6bf45dcea4/pytest-9.0.1.tar.gz", hash = "sha256:3e9c069ea73583e255c3b21cf46b8d3c56f6e3a1a8f6da94ccb0fcf57b9d73c8", size = 1564125, upload-time = "2025-11-12T13:05:09.333Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, + { url = "https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl", hash = "sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad", size = 373668, upload-time = "2025-11-12T13:05:07.379Z" }, ] [[package]] @@ -1634,14 +1704,14 @@ wheels = [ [[package]] name = "pytest-asyncio" -version = "1.2.0" +version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, + { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, ] [[package]] @@ -1747,11 +1817,11 @@ wheels = [ [[package]] name = "redis" -version = "7.0.1" +version = "7.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/8f/f125feec0b958e8d22c8f0b492b30b1991d9499a4315dfde466cf4289edc/redis-7.0.1.tar.gz", hash = "sha256:c949df947dca995dc68fdf5a7863950bf6df24f8d6022394585acc98e81624f1", size = 4755322, upload-time = "2025-10-27T14:34:00.33Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/97/9f22a33c475cda519f20aba6babb340fb2f2254a02fb947816960d1e669a/redis-7.0.1-py3-none-any.whl", hash = "sha256:4977af3c7d67f8f0eb8b6fec0dafc9605db9343142f634041fb0235f67c0588a", size = 339938, upload-time = "2025-10-27T14:33:58.553Z" }, + { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, ] [[package]] @@ -1992,16 +2062,16 @@ wheels = [ [[package]] name = "rich-toolkit" -version = "0.15.1" +version = "0.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/67/33/1a18839aaa8feef7983590c05c22c9c09d245ada6017d118325bbfcc7651/rich_toolkit-0.15.1.tar.gz", hash = "sha256:6f9630eb29f3843d19d48c3bd5706a086d36d62016687f9d0efa027ddc2dd08a", size = 115322, upload-time = "2025-09-04T09:28:11.789Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/8e/ab512afd71d4e67bb611a57db92a0e967304c97ec61963e99103f5a88069/rich_toolkit-0.16.0.tar.gz", hash = "sha256:2f554b00b194776639f4d80f2706980756b659ceed9f345ebbd9de77d1bdd0f4", size = 183790, upload-time = "2025-11-19T15:26:11.431Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/49/42821d55ead7b5a87c8d121edf323cb393d8579f63e933002ade900b784f/rich_toolkit-0.15.1-py3-none-any.whl", hash = "sha256:36a0b1d9a135d26776e4b78f1d5c2655da6e0ef432380b5c6b523c8d8ab97478", size = 29412, upload-time = "2025-09-04T09:28:10.587Z" }, + { url = "https://files.pythonhosted.org/packages/3a/04/f4bfb5d8a258d395d7fb6fbaa0e3fe7bafae17a2a3e2387e6dea9d6474df/rich_toolkit-0.16.0-py3-none-any.whl", hash = "sha256:3f4307f678c5c1e22c36d89ac05f1cd145ed7174f19c1ce5a4d3664ba77c0f9e", size = 29775, upload-time = "2025-11-19T15:26:10.336Z" }, ] [[package]] @@ -2071,53 +2141,53 @@ wheels = [ [[package]] name = "ruff" -version = "0.14.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/55/cccfca45157a2031dcbb5a462a67f7cf27f8b37d4b3b1cd7438f0f5c1df6/ruff-0.14.4.tar.gz", hash = "sha256:f459a49fe1085a749f15414ca76f61595f1a2cc8778ed7c279b6ca2e1fd19df3", size = 5587844, upload-time = "2025-11-06T22:07:45.033Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/b9/67240254166ae1eaa38dec32265e9153ac53645a6c6670ed36ad00722af8/ruff-0.14.4-py3-none-linux_armv6l.whl", hash = "sha256:e6604613ffbcf2297cd5dcba0e0ac9bd0c11dc026442dfbb614504e87c349518", size = 12606781, upload-time = "2025-11-06T22:07:01.841Z" }, - { url = "https://files.pythonhosted.org/packages/46/c8/09b3ab245d8652eafe5256ab59718641429f68681ee713ff06c5c549f156/ruff-0.14.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d99c0b52b6f0598acede45ee78288e5e9b4409d1ce7f661f0fa36d4cbeadf9a4", size = 12946765, upload-time = "2025-11-06T22:07:05.858Z" }, - { url = "https://files.pythonhosted.org/packages/14/bb/1564b000219144bf5eed2359edc94c3590dd49d510751dad26202c18a17d/ruff-0.14.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9358d490ec030f1b51d048a7fd6ead418ed0826daf6149e95e30aa67c168af33", size = 11928120, upload-time = "2025-11-06T22:07:08.023Z" }, - { url = "https://files.pythonhosted.org/packages/a3/92/d5f1770e9988cc0742fefaa351e840d9aef04ec24ae1be36f333f96d5704/ruff-0.14.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b40d27924f1f02dfa827b9c0712a13c0e4b108421665322218fc38caf615c2", size = 12370877, upload-time = "2025-11-06T22:07:10.015Z" }, - { url = "https://files.pythonhosted.org/packages/e2/29/e9282efa55f1973d109faf839a63235575519c8ad278cc87a182a366810e/ruff-0.14.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f5e649052a294fe00818650712083cddc6cc02744afaf37202c65df9ea52efa5", size = 12408538, upload-time = "2025-11-06T22:07:13.085Z" }, - { url = "https://files.pythonhosted.org/packages/8e/01/930ed6ecfce130144b32d77d8d69f5c610e6d23e6857927150adf5d7379a/ruff-0.14.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa082a8f878deeba955531f975881828fd6afd90dfa757c2b0808aadb437136e", size = 13141942, upload-time = "2025-11-06T22:07:15.386Z" }, - { url = "https://files.pythonhosted.org/packages/6a/46/a9c89b42b231a9f487233f17a89cbef9d5acd538d9488687a02ad288fa6b/ruff-0.14.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1043c6811c2419e39011890f14d0a30470f19d47d197c4858b2787dfa698f6c8", size = 14544306, upload-time = "2025-11-06T22:07:17.631Z" }, - { url = "https://files.pythonhosted.org/packages/78/96/9c6cf86491f2a6d52758b830b89b78c2ae61e8ca66b86bf5a20af73d20e6/ruff-0.14.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a9f3a936ac27fb7c2a93e4f4b943a662775879ac579a433291a6f69428722649", size = 14210427, upload-time = "2025-11-06T22:07:19.832Z" }, - { url = "https://files.pythonhosted.org/packages/71/f4/0666fe7769a54f63e66404e8ff698de1dcde733e12e2fd1c9c6efb689cb5/ruff-0.14.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95643ffd209ce78bc113266b88fba3d39e0461f0cbc8b55fb92505030fb4a850", size = 13658488, upload-time = "2025-11-06T22:07:22.32Z" }, - { url = "https://files.pythonhosted.org/packages/ee/79/6ad4dda2cfd55e41ac9ed6d73ef9ab9475b1eef69f3a85957210c74ba12c/ruff-0.14.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:456daa2fa1021bc86ca857f43fe29d5d8b3f0e55e9f90c58c317c1dcc2afc7b5", size = 13354908, upload-time = "2025-11-06T22:07:24.347Z" }, - { url = "https://files.pythonhosted.org/packages/b5/60/f0b6990f740bb15c1588601d19d21bcc1bd5de4330a07222041678a8e04f/ruff-0.14.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f911bba769e4a9f51af6e70037bb72b70b45a16db5ce73e1f72aefe6f6d62132", size = 13587803, upload-time = "2025-11-06T22:07:26.327Z" }, - { url = "https://files.pythonhosted.org/packages/c9/da/eaaada586f80068728338e0ef7f29ab3e4a08a692f92eb901a4f06bbff24/ruff-0.14.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:76158a7369b3979fa878612c623a7e5430c18b2fd1c73b214945c2d06337db67", size = 12279654, upload-time = "2025-11-06T22:07:28.46Z" }, - { url = "https://files.pythonhosted.org/packages/66/d4/b1d0e82cf9bf8aed10a6d45be47b3f402730aa2c438164424783ac88c0ed/ruff-0.14.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f3b8f3b442d2b14c246e7aeca2e75915159e06a3540e2f4bed9f50d062d24469", size = 12357520, upload-time = "2025-11-06T22:07:31.468Z" }, - { url = "https://files.pythonhosted.org/packages/04/f4/53e2b42cc82804617e5c7950b7079d79996c27e99c4652131c6a1100657f/ruff-0.14.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c62da9a06779deecf4d17ed04939ae8b31b517643b26370c3be1d26f3ef7dbde", size = 12719431, upload-time = "2025-11-06T22:07:33.831Z" }, - { url = "https://files.pythonhosted.org/packages/a2/94/80e3d74ed9a72d64e94a7b7706b1c1ebaa315ef2076fd33581f6a1cd2f95/ruff-0.14.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5a443a83a1506c684e98acb8cb55abaf3ef725078be40237463dae4463366349", size = 13464394, upload-time = "2025-11-06T22:07:35.905Z" }, - { url = "https://files.pythonhosted.org/packages/54/1a/a49f071f04c42345c793d22f6cf5e0920095e286119ee53a64a3a3004825/ruff-0.14.4-py3-none-win32.whl", hash = "sha256:643b69cb63cd996f1fc7229da726d07ac307eae442dd8974dbc7cf22c1e18fff", size = 12493429, upload-time = "2025-11-06T22:07:38.43Z" }, - { url = "https://files.pythonhosted.org/packages/bc/22/e58c43e641145a2b670328fb98bc384e20679b5774258b1e540207580266/ruff-0.14.4-py3-none-win_amd64.whl", hash = "sha256:26673da283b96fe35fa0c939bf8411abec47111644aa9f7cfbd3c573fb125d2c", size = 13635380, upload-time = "2025-11-06T22:07:40.496Z" }, - { url = "https://files.pythonhosted.org/packages/30/bd/4168a751ddbbf43e86544b4de8b5c3b7be8d7167a2a5cb977d274e04f0a1/ruff-0.14.4-py3-none-win_arm64.whl", hash = "sha256:dd09c292479596b0e6fec8cd95c65c3a6dc68e9ad17b8f2382130f87ff6a75bb", size = 12663065, upload-time = "2025-11-06T22:07:42.603Z" }, +version = "0.14.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/f0/62b5a1a723fe183650109407fa56abb433b00aa1c0b9ba555f9c4efec2c6/ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc", size = 5669501, upload-time = "2025-11-21T14:26:17.903Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/d2/7dd544116d107fffb24a0064d41a5d2ed1c9d6372d142f9ba108c8e39207/ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3", size = 13326119, upload-time = "2025-11-21T14:25:24.2Z" }, + { url = "https://files.pythonhosted.org/packages/36/6a/ad66d0a3315d6327ed6b01f759d83df3c4d5f86c30462121024361137b6a/ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004", size = 13526007, upload-time = "2025-11-21T14:25:26.906Z" }, + { url = "https://files.pythonhosted.org/packages/a3/9d/dae6db96df28e0a15dea8e986ee393af70fc97fd57669808728080529c37/ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332", size = 12676572, upload-time = "2025-11-21T14:25:29.826Z" }, + { url = "https://files.pythonhosted.org/packages/76/a4/f319e87759949062cfee1b26245048e92e2acce900ad3a909285f9db1859/ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef", size = 13140745, upload-time = "2025-11-21T14:25:32.788Z" }, + { url = "https://files.pythonhosted.org/packages/95/d3/248c1efc71a0a8ed4e8e10b4b2266845d7dfc7a0ab64354afe049eaa1310/ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775", size = 13076486, upload-time = "2025-11-21T14:25:35.601Z" }, + { url = "https://files.pythonhosted.org/packages/a5/19/b68d4563fe50eba4b8c92aa842149bb56dd24d198389c0ed12e7faff4f7d/ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce", size = 13727563, upload-time = "2025-11-21T14:25:38.514Z" }, + { url = "https://files.pythonhosted.org/packages/47/ac/943169436832d4b0e867235abbdb57ce3a82367b47e0280fa7b4eabb7593/ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f", size = 15199755, upload-time = "2025-11-21T14:25:41.516Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b9/288bb2399860a36d4bb0541cb66cce3c0f4156aaff009dc8499be0c24bf2/ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d", size = 14850608, upload-time = "2025-11-21T14:25:44.428Z" }, + { url = "https://files.pythonhosted.org/packages/ee/b1/a0d549dd4364e240f37e7d2907e97ee80587480d98c7799d2d8dc7a2f605/ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440", size = 14118754, upload-time = "2025-11-21T14:25:47.214Z" }, + { url = "https://files.pythonhosted.org/packages/13/ac/9b9fe63716af8bdfddfacd0882bc1586f29985d3b988b3c62ddce2e202c3/ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105", size = 13949214, upload-time = "2025-11-21T14:25:50.002Z" }, + { url = "https://files.pythonhosted.org/packages/12/27/4dad6c6a77fede9560b7df6802b1b697e97e49ceabe1f12baf3ea20862e9/ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821", size = 14106112, upload-time = "2025-11-21T14:25:52.841Z" }, + { url = "https://files.pythonhosted.org/packages/6a/db/23e322d7177873eaedea59a7932ca5084ec5b7e20cb30f341ab594130a71/ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55", size = 13035010, upload-time = "2025-11-21T14:25:55.536Z" }, + { url = "https://files.pythonhosted.org/packages/a8/9c/20e21d4d69dbb35e6a1df7691e02f363423658a20a2afacf2a2c011800dc/ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71", size = 13054082, upload-time = "2025-11-21T14:25:58.625Z" }, + { url = "https://files.pythonhosted.org/packages/66/25/906ee6a0464c3125c8d673c589771a974965c2be1a1e28b5c3b96cb6ef88/ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b", size = 13303354, upload-time = "2025-11-21T14:26:01.816Z" }, + { url = "https://files.pythonhosted.org/packages/4c/58/60577569e198d56922b7ead07b465f559002b7b11d53f40937e95067ca1c/ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185", size = 14054487, upload-time = "2025-11-21T14:26:05.058Z" }, + { url = "https://files.pythonhosted.org/packages/67/0b/8e4e0639e4cc12547f41cb771b0b44ec8225b6b6a93393176d75fe6f7d40/ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85", size = 13013361, upload-time = "2025-11-21T14:26:08.152Z" }, + { url = "https://files.pythonhosted.org/packages/fb/02/82240553b77fd1341f80ebb3eaae43ba011c7a91b4224a9f317d8e6591af/ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9", size = 14432087, upload-time = "2025-11-21T14:26:10.891Z" }, + { url = "https://files.pythonhosted.org/packages/a5/1f/93f9b0fad9470e4c829a5bb678da4012f0c710d09331b860ee555216f4ea/ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2", size = 13520930, upload-time = "2025-11-21T14:26:13.951Z" }, ] [[package]] name = "s3transfer" -version = "0.14.0" +version = "0.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/62/74/8d69dcb7a9efe8baa2046891735e5dfe433ad558ae23d9e3c14c633d1d58/s3transfer-0.14.0.tar.gz", hash = "sha256:eff12264e7c8b4985074ccce27a3b38a485bb7f7422cc8046fee9be4983e4125", size = 151547, upload-time = "2025-09-09T19:23:31.089Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bb/940d6af975948c1cc18f44545ffb219d3c35d78ec972b42ae229e8e37e08/s3transfer-0.15.0.tar.gz", hash = "sha256:d36fac8d0e3603eff9b5bfa4282c7ce6feb0301a633566153cbd0b93d11d8379", size = 152185, upload-time = "2025-11-20T20:28:56.327Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/f0/ae7ca09223a81a1d890b2557186ea015f6e0502e9b8cb8e1813f1d8cfa4e/s3transfer-0.14.0-py3-none-any.whl", hash = "sha256:ea3b790c7077558ed1f02a3072fb3cb992bbbd253392f4b6e9e8976941c7d456", size = 85712, upload-time = "2025-09-09T19:23:30.041Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e1/5ef25f52973aa12a19cf4e1375d00932d7fb354ffd310487ba7d44225c1a/s3transfer-0.15.0-py3-none-any.whl", hash = "sha256:6f8bf5caa31a0865c4081186689db1b2534cef721d104eb26101de4b9d6a5852", size = 85984, upload-time = "2025-11-20T20:28:55.046Z" }, ] [[package]] name = "sentry-sdk" -version = "2.43.0" +version = "2.46.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/18/09875b4323b03ca9025bae7e6539797b27e4fc032998a466b4b9c3d24653/sentry_sdk-2.43.0.tar.gz", hash = "sha256:52ed6e251c5d2c084224d73efee56b007ef5c2d408a4a071270e82131d336e20", size = 368953, upload-time = "2025-10-29T11:26:08.156Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/d7/c140a5837649e2bf2ec758494fde1d9a016c76777eab64e75ef38d685bbb/sentry_sdk-2.46.0.tar.gz", hash = "sha256:91821a23460725734b7741523021601593f35731808afc0bb2ba46c27b8acd91", size = 374761, upload-time = "2025-11-24T09:34:13.932Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/31/8228fa962f7fd8814d634e4ebece8780e2cdcfbdf0cd2e14d4a6861a7cd5/sentry_sdk-2.43.0-py2.py3-none-any.whl", hash = "sha256:4aacafcf1756ef066d359ae35030881917160ba7f6fc3ae11e0e58b09edc2d5d", size = 400997, upload-time = "2025-10-29T11:26:05.77Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b6/ce7c502a366f4835b1f9c057753f6989a92d3c70cbadb168193f5fb7499b/sentry_sdk-2.46.0-py2.py3-none-any.whl", hash = "sha256:4eeeb60198074dff8d066ea153fa6f241fef1668c10900ea53a4200abc8da9b1", size = 406266, upload-time = "2025-11-24T09:34:12.114Z" }, ] [[package]] @@ -2158,7 +2228,7 @@ wheels = [ [[package]] name = "sqladmin" -version = "0.21.0" +version = "0.22.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jinja2" }, @@ -2167,9 +2237,9 @@ dependencies = [ { name = "starlette" }, { name = "wtforms" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/0c/614041e1b544e0de1f43b58f0105b3e2795b80369d5b0ff7412882d42fff/sqladmin-0.21.0.tar.gz", hash = "sha256:cb455b79eb79ef7d904680dd83817bf7750675147400b5b7cc401d04bda7ef2c", size = 1428312, upload-time = "2025-07-02T09:41:21.207Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/ac/526bb3ff2dd94fbf8442bccb49ef40aa360045add19d4fbffcb43995e67a/sqladmin-0.22.0.tar.gz", hash = "sha256:4ea904d97e4d030edb68fb0681330b4d963f422442a64bee487fdc46119b3729", size = 1429937, upload-time = "2025-11-24T12:52:59.285Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/8d/81b2a48cc6f5479cb1148292518e3006ec8f5fbe3b0829ef165984e9d7b9/sqladmin-0.21.0-py3-none-any.whl", hash = "sha256:2b1802c49bdd3128c6452625705693cf32d5d33e7db30e63f409bd20a9c05b53", size = 1443585, upload-time = "2025-07-02T09:41:19.205Z" }, + { url = "https://files.pythonhosted.org/packages/f2/b4/ab78c7d7b13bd3f90d6d8a106c5ad12bf7a738f89eb0241b24ad8efe5d1e/sqladmin-0.22.0-py3-none-any.whl", hash = "sha256:f2fb11165a70601a97f71956104b47da2c432db49b0d7966dc65e9e6343887d3", size = 1445514, upload-time = "2025-11-24T12:53:00.511Z" }, ] [[package]] @@ -2213,14 +2283,14 @@ wheels = [ [[package]] name = "starlette" -version = "0.49.3" +version = "0.50.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/1a/608df0b10b53b0beb96a37854ee05864d182ddd4b1156a22f1ad3860425a/starlette-0.49.3.tar.gz", hash = "sha256:1c14546f299b5901a1ea0e34410575bc33bbd741377a10484a54445588d00284", size = 2655031, upload-time = "2025-11-01T15:12:26.13Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/e0/021c772d6a662f43b63044ab481dc6ac7592447605b5b35a957785363122/starlette-0.49.3-py3-none-any.whl", hash = "sha256:b579b99715fdc2980cf88c8ec96d3bf1ce16f5a8051a7c2b84ef9b1cdecaea2f", size = 74340, upload-time = "2025-11-01T15:12:24.387Z" }, + { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, ] [[package]] diff --git a/codemeta.json b/codemeta.json index 9d940b3..caa27b5 100644 --- a/codemeta.json +++ b/codemeta.json @@ -42,7 +42,7 @@ ], "license": "https://spdx.org/licenses/AGPL-3.0-or-later", "name": "Reverse Engineering Lab", - "programmingLanguage": ["Python 3.13", "JavaScript"], + "programmingLanguage": ["Python 3", "JavaScript"], "softwareRequirements": "Docker", "version": "0.1.0" } From 04715fea042129618164d76808192df69c383b8c Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 25 Nov 2025 10:23:09 +0000 Subject: [PATCH 55/79] chore: linting --- .github/ISSUE_TEMPLATE/bug_report.md | 6 +- .github/ISSUE_TEMPLATE/feature-request.md | 6 +- .github/ISSUE_TEMPLATE/internal-ticket.md | 6 +- .pre-commit-config.yaml | 93 ++++++------- CONTRIBUTING.md | 12 +- backend/.dockerignore | 2 +- backend/.gitignore | 2 +- backend/app/api/auth/services/user_manager.py | 6 +- .../app/api/auth/utils/email_validation.py | 2 +- .../app/api/auth/utils/programmatic_emails.py | 13 +- .../app/api/common/schemas/custom_fields.py | 8 +- backend/app/api/data_collection/routers.py | 10 +- backend/tests/conftest.py | 2 +- compose.prod.yml | 4 +- frontend-app/src/app/products/[id]/index.tsx | 8 +- .../src/services/api/validation/product.ts | 2 +- uv.lock | 126 ++++++++++-------- 17 files changed, 165 insertions(+), 143 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 0d8647c..73bc3bd 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,9 +1,9 @@ --- -name: Bug report about: Create a report to help us improve -title: 'bug: ' -labels: bug assignees: '' +labels: bug +name: Bug report +title: 'bug: ' --- ## Bug description diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md index eea6c5b..4d1d87f 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -1,9 +1,9 @@ --- -name: Feature request about: Suggest an idea for this project -title: 'feature request: ' -labels: feature request assignees: '' +labels: feature request +name: Feature request +title: 'feature request: ' --- ## Problem statement diff --git a/.github/ISSUE_TEMPLATE/internal-ticket.md b/.github/ISSUE_TEMPLATE/internal-ticket.md index e687334..bb764f7 100644 --- a/.github/ISSUE_TEMPLATE/internal-ticket.md +++ b/.github/ISSUE_TEMPLATE/internal-ticket.md @@ -1,9 +1,9 @@ --- -name: Internal ticket about: For internal development -title: '' -labels: '' assignees: '' +labels: '' +name: Internal ticket +title: '' --- ## Problem diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a6b2c07..ce5a11e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,81 +5,82 @@ repos: ### Global hooks - - repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update +- repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update rev: v0.9.0 hooks: - - id: pre-commit-update # Autoupdate pre-commit hooks + - id: pre-commit-update # Autoupdate pre-commit hooks - - repo: https://github.com/gitleaks/gitleaks - rev: v8.29.0 +- repo: https://github.com/gitleaks/gitleaks + rev: v8.29.1 hooks: - - id: gitleaks + - id: gitleaks - - repo: https://github.com/executablebooks/mdformat +- repo: https://github.com/executablebooks/mdformat rev: 1.0.0 hooks: - - id: mdformat # Format Markdown files. + - id: mdformat # Format Markdown files. additional_dependencies: - - mdformat-gfm>=1.0.0 # Support GitHub Flavored Markdown. - - mdformat-ruff # Support Python code blocks linted with Ruff. + - mdformat-gfm>=1.0.0 # Support GitHub Flavored Markdown. + - mdformat-front-matters + - mdformat-ruff # Support Python code blocks linted with Ruff. - - repo: https://github.com/pre-commit/pre-commit-hooks +- repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - - id: check-added-large-files - - id: check-case-conflict # Check for files with names that differ only in case. - - id: check-executables-have-shebangs - - id: check-shebang-scripts-are-executable - - id: check-toml - - id: check-yaml + - id: check-added-large-files + - id: check-case-conflict # Check for files with names that differ only in case. + - id: check-executables-have-shebangs + - id: check-shebang-scripts-are-executable + - id: check-toml + - id: check-yaml exclude: ^docs/mkdocs.yml$ # Exclude mkdocs.yml because it uses an obscure tag to allow for mermaid formatting - - id: detect-private-key - - id: end-of-file-fixer # Ensure files end with a newline. - - id: mixed-line-ending - - id: no-commit-to-branch # Prevent commits to main and master branches. - - id: trailing-whitespace + - id: detect-private-key + - id: end-of-file-fixer # Ensure files end with a newline. + - id: mixed-line-ending + - id: no-commit-to-branch # Prevent commits to main and master branches. + - id: trailing-whitespace args: ["--markdown-linebreak-ext", "md"] # Preserve Markdown hard line breaks. - - repo: https://github.com/commitizen-tools/commitizen - rev: v4.9.1 +- repo: https://github.com/commitizen-tools/commitizen + rev: v4.10.0 hooks: - - id: commitizen + - id: commitizen stages: [commit-msg] - - repo: https://github.com/simonvanlierde/check-json5 +- repo: https://github.com/simonvanlierde/check-json5 rev: v1.1.0 hooks: - - id: check-json5 + - id: check-json5 files: ^ (?!(backend/frontend-app|frontend-web)/data/) ### Backend hooks - - repo: https://github.com/RobertCraigie/pyright-python # Lint backend code with Pyright. +- repo: https://github.com/RobertCraigie/pyright-python # Lint backend code with Pyright. rev: v1.1.407 hooks: - - id: pyright + - id: pyright files: ^backend/(app|scripts|tests)/ entry: pyright --project backend - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.4 +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.14.6 hooks: - - id: ruff-check # Lint code + - id: ruff-check # Lint code files: ^backend/(app|scripts|tests)/ args: ["--fix", "--config", "backend/pyproject.toml", "--ignore", "FIX002"] # Allow TODO comments in commits. - - id: ruff-format # Format code + - id: ruff-format # Format code files: ^backend/(app|scripts|tests)/ args: ["--config", "backend/pyproject.toml"] - - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.8 +- repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.9.11 hooks: - - id: uv-lock # Update the uv lockfile for the backend. + - id: uv-lock # Update the uv lockfile for the backend. files: ^backend/(uv\.lock|pyproject\.toml|uv\.toml)$ entry: uv lock --project backend - - repo: local +- repo: local hooks: # Check if Alembic migrations are up-to-date. Uses uv to ensure the right environment when executed through VS Code Git extension. - - id: backend-alembic-autogen-check + - id: backend-alembic-autogen-check name: check alembic migrations entry: bash -c 'cd backend && uv run alembic-autogen-check' language: system @@ -88,21 +89,21 @@ repos: stages: [pre-commit] ### Frontend hooks - - repo: local +- repo: local hooks: - - id: frontend-web-format + - id: frontend-web-format name: format frontend-web code entry: bash -c 'cd frontend-web && npm run format' - language: - system + language: system # Match frontend JavaScript and TypeScript files for formatting. - files: ^frontend-web\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ + files: + ^frontend-web\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ pass_filenames: false - - id: frontend-app-format + - id: frontend-app-format name: format frontend-app code entry: bash -c 'cd frontend-app && npm run format' - language: - system + language: system # Match frontend JavaScript and TypeScript files for formatting. - files: ^frontend-app\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ + files: + ^frontend-app\/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|d\\.(ts|cts|mts)|jsonc?)$ pass_filenames: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b7c8b97..b151b2d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -345,23 +345,25 @@ When making changes to the database schema: This project uses [MJML](https://mjml.io/) to write email templates and [Jinja2](https://jinja.palletsprojects.com/en/latest/) for variable substitution at runtime. - **Location** + - Source MJML templates: `backend/app/templates/emails/src/` - Reusable components: `backend/app/templates/emails/src/components/` - Compiled HTML output: `backend/app/templates/emails/build/` (This directory is **auto-generated**—do not edit files here.) - **Editing Guidelines** + - Use **MJML** for structure and the `{{include:component_name}}` directive to reuse components. - Use **Jinja2-style variables** in templates, e.g., `{{ username }}`, `{{ verification_link }}`. - Keep components small and shared styles in `src/components/styles.mjml`. - **Do not modify** files in `build/`. - **Compiling Templates** - Run the compilation script from the repository root: + Run the compilation script from the repository root: - ```bash - cd backend - python scripts/compile_email_templates.py - ``` + ```bash + cd backend + python scripts/compile_email_templates.py + ``` - **Interactive Preview** For visual development, use MJML online tools or the [MJML VS Code extension](https://marketplace.visualstudio.com/items?itemName=mjmlio.vscode-mjml). diff --git a/backend/.dockerignore b/backend/.dockerignore index 2c42c71..08aa94a 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -98,4 +98,4 @@ MANIFEST ./logs # Include built email templates -!app/templates/emails/build/ \ No newline at end of file +!app/templates/emails/build/ diff --git a/backend/.gitignore b/backend/.gitignore index 966df64..fb372f9 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -82,4 +82,4 @@ backups/* !.vscode/extensions.json # Include built email templates -!app/templates/emails/build/ \ No newline at end of file +!app/templates/emails/build/ diff --git a/backend/app/api/auth/services/user_manager.py b/backend/app/api/auth/services/user_manager.py index f0ab70d..40eab49 100644 --- a/backend/app/api/auth/services/user_manager.py +++ b/backend/app/api/auth/services/user_manager.py @@ -64,7 +64,11 @@ async def create( email_checker = None else: # Get email checker from app state if request is available - email_checker = request.app.state.email_checker if (request and request.app and hasattr(request.app.state, "email_checker")) else None + email_checker = ( + request.app.state.email_checker + if (request and request.app and hasattr(request.app.state, "email_checker")) + else None + ) try: user_create = await create_user_override(self.user_db, user_create, email_checker) diff --git a/backend/app/api/auth/utils/email_validation.py b/backend/app/api/auth/utils/email_validation.py index 6d5275f..5e98026 100644 --- a/backend/app/api/auth/utils/email_validation.py +++ b/backend/app/api/auth/utils/email_validation.py @@ -42,7 +42,7 @@ async def initialize(self) -> None: source=DISPOSABLE_DOMAINS_URL, db_provider="redis", redis_client=self.redis_client, - ) + ) await self.checker.init_redis() logger.info("Disposable email checker initialized with Redis") diff --git a/backend/app/api/auth/utils/programmatic_emails.py b/backend/app/api/auth/utils/programmatic_emails.py index b5217f8..6c02f37 100644 --- a/backend/app/api/auth/utils/programmatic_emails.py +++ b/backend/app/api/auth/utils/programmatic_emails.py @@ -22,19 +22,20 @@ def generate_token_link(token: str, route: str, base_url: str | AnyUrl | None = base_url = str(core_settings.frontend_app_url) return urljoin(str(base_url), f"{route}?token={token}") + def mask_email_for_log(email: EmailStr, mask: bool = True, max_len: int = 80) -> str: """Mask emails for logging. - + Also remove non-printable characters and truncates long domains. Explicitly removes log-breaking control characters. """ # Remove non-printable and log-breaking control characters - string = "".join(ch for ch in str(email) if ch.isprintable()).replace('\n', '').replace('\r', '') + string = "".join(ch for ch in str(email) if ch.isprintable()).replace("\n", "").replace("\r", "") local, sep, domain = string.partition("@") if sep and mask: - masked = (f"{local[0]}***@{domain}" if len(local) > 1 else f"*@{domain}") + masked = f"{local[0]}***@{domain}" if len(local) > 1 else f"*@{domain}" else: masked = string - return (f"{masked[:max_len-3]}..." if len(masked) > max_len else masked) + return f"{masked[: max_len - 3]}..." if len(masked) > max_len else masked ### Generic email function ### @@ -63,7 +64,9 @@ async def send_email_with_template( if background_tasks: background_tasks.add_task(fm.send_message, message, template_name=template_name) - logger.info("Email queued for background sending to %s using template %s", mask_email_for_log(to_email), template_name) + logger.info( + "Email queued for background sending to %s using template %s", mask_email_for_log(to_email), template_name + ) else: await fm.send_message(message, template_name=template_name) logger.info("Email sent to %s using template %s", mask_email_for_log(to_email), template_name) diff --git a/backend/app/api/common/schemas/custom_fields.py b/backend/app/api/common/schemas/custom_fields.py index 5614bd2..7fd682e 100644 --- a/backend/app/api/common/schemas/custom_fields.py +++ b/backend/app/api/common/schemas/custom_fields.py @@ -5,9 +5,5 @@ from pydantic import AnyUrl, HttpUrl, PlainSerializer, StringConstraints # HTTP URL that is stored as string in the database. -HttpUrlToDB: TypeAlias = Annotated[ - HttpUrl, PlainSerializer(str, return_type=str), StringConstraints(max_length=250) -] -AnyUrlToDB: TypeAlias = Annotated[ - AnyUrl, PlainSerializer(str, return_type=str), StringConstraints(max_length=250) -] +HttpUrlToDB: TypeAlias = Annotated[HttpUrl, PlainSerializer(str, return_type=str), StringConstraints(max_length=250)] +AnyUrlToDB: TypeAlias = Annotated[AnyUrl, PlainSerializer(str, return_type=str), StringConstraints(max_length=250)] diff --git a/backend/app/api/data_collection/routers.py b/backend/app/api/data_collection/routers.py index 8fdc0f9..58b5e52 100644 --- a/backend/app/api/data_collection/routers.py +++ b/backend/app/api/data_collection/routers.py @@ -149,9 +149,9 @@ async def get_user_products( # NOTE: If needed, we can open up this endpoint to any user by removing this ownership check if user_id != current_user.id and not current_user.is_superuser: raise HTTPException(status_code=403, detail="Not authorized to view this user's products") - - statement=(select(Product).where(Product.owner_id == user_id)) - + + statement = select(Product).where(Product.owner_id == user_id) + if not include_components_as_base_products: statement: SelectOfScalar[Product] = statement.where(Product.parent_id == None) @@ -800,7 +800,9 @@ async def delete_product_physical_properties( response_model=CircularityPropertiesRead, summary="Get product circularity properties", ) -async def get_product_circularity_properties(product_id: PositiveInt, session: AsyncSessionDep) -> CircularityProperties: +async def get_product_circularity_properties( + product_id: PositiveInt, session: AsyncSessionDep +) -> CircularityProperties: """Get circularity properties for a product.""" return await crud.get_circularity_properties(session, product_id) diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 5deafde..843680c 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -9,7 +9,6 @@ from unittest.mock import AsyncMock import pytest -from alembic.config import Config from fastapi.testclient import TestClient from sqlalchemy import Engine, create_engine, text from sqlalchemy.exc import ProgrammingError @@ -18,6 +17,7 @@ from sqlmodel.ext.asyncio.session import AsyncSession from alembic import command +from alembic.config import Config from app.core.config import settings from app.main import app diff --git a/compose.prod.yml b/compose.prod.yml index 866eb99..12e7dd7 100644 --- a/compose.prod.yml +++ b/compose.prod.yml @@ -35,8 +35,8 @@ services: volumes: # Persist cache data in production - cache_data:/data - cloudflared: # Cloudflared tunnel to cml-relab.org - image: cloudflare/cloudflared:latest@sha256:396cd2e6f021275ad09969a1b4f1a7e62ca5349fde62781ce082bb2c18105c70 + cloudflared: # Cloudflared tunnel service + image: cloudflare/cloudflared:latest@sha256:89ee50efb1e9cb2ae30281a8a404fed95eb8f02f0a972617526f8c5b417acae2 command: tunnel --no-autoupdate run env_file: .env # Should contain TUNNEL_TOKEN variable pull_policy: always diff --git a/frontend-app/src/app/products/[id]/index.tsx b/frontend-app/src/app/products/[id]/index.tsx index 201b7c1..3c59cce 100644 --- a/frontend-app/src/app/products/[id]/index.tsx +++ b/frontend-app/src/app/products/[id]/index.tsx @@ -260,11 +260,7 @@ export default function ProductPage(): JSX.Element { - + Edit name; -} \ No newline at end of file +} diff --git a/frontend-app/src/services/api/validation/product.ts b/frontend-app/src/services/api/validation/product.ts index 8639dda..b55c6b0 100644 --- a/frontend-app/src/services/api/validation/product.ts +++ b/frontend-app/src/services/api/validation/product.ts @@ -139,4 +139,4 @@ export function validateProduct(product: Product): ValidationResult { } return { isValid: true }; -} \ No newline at end of file +} diff --git a/uv.lock b/uv.lock index a14c571..22883f8 100644 --- a/uv.lock +++ b/uv.lock @@ -4,20 +4,20 @@ requires-python = ">=3.13" [[package]] name = "argcomplete" -version = "3.6.2" +version = "3.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/16/0f/861e168fc813c56a78b35f3c30d91c6757d1fd185af1110f1aec784b35d0/argcomplete-3.6.2.tar.gz", hash = "sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf", size = 73403, upload-time = "2025-04-03T04:57:03.52Z" } +sdist = { url = "https://files.pythonhosted.org/packages/38/61/0b9ae6399dd4a58d8c1b1dc5a27d6f2808023d0b5dd3104bb99f45a33ff6/argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c", size = 73754, upload-time = "2025-10-20T03:33:34.741Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/da/e42d7a9d8dd33fa775f467e4028a47936da2f01e4b0e561f9ba0d74cb0ca/argcomplete-3.6.2-py3-none-any.whl", hash = "sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591", size = 43708, upload-time = "2025-04-03T04:57:01.591Z" }, + { url = "https://files.pythonhosted.org/packages/74/f5/9373290775639cb67a2fce7f629a1c240dce9f12fe927bc32b2736e16dfc/argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce", size = 43846, upload-time = "2025-10-20T03:33:33.021Z" }, ] [[package]] name = "cfgv" -version = "3.4.0" +version = "3.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } +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/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, + { 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]] @@ -72,7 +72,7 @@ wheels = [ [[package]] name = "commitizen" -version = "4.9.1" +version = "4.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argcomplete" }, @@ -88,9 +88,9 @@ dependencies = [ { name = "termcolor" }, { name = "tomlkit" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/19/927ac5b0eabb9451e2d5bb45b30813915c9a1260713b5b68eeb31358ea23/commitizen-4.9.1.tar.gz", hash = "sha256:b076b24657718f7a35b1068f2083bd39b4065d250164a1398d1dac235c51753b", size = 56610, upload-time = "2025-09-10T14:19:33.746Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/b3/cc29794fc2ecd7aa7353105773ca18ecd761c3ba5b38879bd106b3fc8840/commitizen-4.10.0.tar.gz", hash = "sha256:cc58067403b9eff21d0423b3d9a29bda05254bd51ad5bdd1fd0594bff31277e1", size = 56820, upload-time = "2025-11-10T14:08:49.365Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/49/577035b841442fe031b017027c3d99278b46104d227f0353c69dbbe55148/commitizen-4.9.1-py3-none-any.whl", hash = "sha256:4241b2ecae97b8109af8e587c36bc3b805a09b9a311084d159098e12d6ead497", size = 80624, upload-time = "2025-09-10T14:19:32.102Z" }, + { url = "https://files.pythonhosted.org/packages/b3/5d/2bd8881737d6a5652ae3ebc37736893b9a7425f0eb16e605d1ff2957267e/commitizen-4.10.0-py3-none-any.whl", hash = "sha256:3fe56c168b30b30b84b8329cba6b132e77b4eb304a5460bbe2186aad0f78c966", size = 81269, upload-time = "2025-11-10T14:08:48.001Z" }, ] [[package]] @@ -104,14 +104,14 @@ wheels = [ [[package]] name = "deprecated" -version = "1.2.18" +version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744, upload-time = "2025-01-27T10:46:25.7Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" }, + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, ] [[package]] @@ -234,7 +234,7 @@ wheels = [ [[package]] name = "pre-commit" -version = "4.3.0" +version = "4.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cfgv" }, @@ -243,9 +243,9 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/9b/6a4ffb4ed980519da959e1cf3122fc6cb41211daa58dbae1c73c0e519a37/pre_commit-4.5.0.tar.gz", hash = "sha256:dc5a065e932b19fc1d4c653c6939068fe54325af8e741e74e88db4d28a4dd66b", size = 198428, upload-time = "2025-11-22T21:02:42.304Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/c4/b2d28e9d2edf4f1713eb3c29307f1a63f3d67cf09bdda29715a36a68921a/pre_commit-4.5.0-py2.py3-none-any.whl", hash = "sha256:25e2ce09595174d9c97860a95609f9f852c0614ba602de3561e267547f2335e1", size = 226429, upload-time = "2025-11-22T21:02:40.836Z" }, ] [[package]] @@ -329,11 +329,11 @@ dev = [ [[package]] name = "termcolor" -version = "3.1.0" +version = "3.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/6c/3d75c196ac07ac8749600b60b03f4f6094d54e132c4d94ebac6ee0e0add0/termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970", size = 14324, upload-time = "2025-04-30T11:37:53.791Z" } +sdist = { url = "https://files.pythonhosted.org/packages/87/56/ab275c2b56a5e2342568838f0d5e3e66a32354adcc159b495e374cda43f5/termcolor-3.2.0.tar.gz", hash = "sha256:610e6456feec42c4bcd28934a8c87a06c3fa28b01561d46aa09a9881b8622c58", size = 14423, upload-time = "2025-10-25T19:11:42.586Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/bd/de8d508070629b6d84a30d01d57e4a65c69aa7f5abe7560b8fad3b50ea59/termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa", size = 7684, upload-time = "2025-04-30T11:37:52.382Z" }, + { url = "https://files.pythonhosted.org/packages/f9/d5/141f53d7c1eb2a80e6d3e9a390228c3222c27705cbe7f048d3623053f3ca/termcolor-3.2.0-py3-none-any.whl", hash = "sha256:a10343879eba4da819353c55cb8049b0933890c2ebf9ad5d3ecd2bb32ea96ea6", size = 7698, upload-time = "2025-10-25T19:11:41.536Z" }, ] [[package]] @@ -347,16 +347,16 @@ wheels = [ [[package]] name = "virtualenv" -version = "20.35.3" +version = "20.35.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a4/d5/b0ccd381d55c8f45d46f77df6ae59fbc23d19e901e2d523395598e5f4c93/virtualenv-20.35.3.tar.gz", hash = "sha256:4f1a845d131133bdff10590489610c98c168ff99dc75d6c96853801f7f67af44", size = 6002907, upload-time = "2025-10-10T21:23:33.178Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/73/d9a94da0e9d470a543c1b9d3ccbceb0f59455983088e727b8a1824ed90fb/virtualenv-20.35.3-py3-none-any.whl", hash = "sha256:63d106565078d8c8d0b206d48080f938a8b25361e19432d2c9db40d2899c810a", size = 5981061, upload-time = "2025-10-10T21:23:30.433Z" }, + { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" }, ] [[package]] @@ -370,39 +370,57 @@ wheels = [ [[package]] name = "wrapt" -version = "1.17.3" +version = "2.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2a/6de8a50cb435b7f42c46126cf1a54b2aab81784e74c8595c8e025e8f36d3/wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f", size = 82040, upload-time = "2025-11-07T00:45:33.312Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, - { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, - { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, - { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, - { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, - { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, - { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, - { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/41af4c46b5e498c90fc87981ab2972fbd9f0bccda597adb99d3d3441b94b/wrapt-2.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:47b0f8bafe90f7736151f61482c583c86b0693d80f075a58701dd1549b0010a9", size = 78132, upload-time = "2025-11-07T00:44:04.628Z" }, + { url = "https://files.pythonhosted.org/packages/1c/92/d68895a984a5ebbbfb175512b0c0aad872354a4a2484fbd5552e9f275316/wrapt-2.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cbeb0971e13b4bd81d34169ed57a6dda017328d1a22b62fda45e1d21dd06148f", size = 61211, upload-time = "2025-11-07T00:44:05.626Z" }, + { url = "https://files.pythonhosted.org/packages/e8/26/ba83dc5ae7cf5aa2b02364a3d9cf74374b86169906a1f3ade9a2d03cf21c/wrapt-2.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb7cffe572ad0a141a7886a1d2efa5bef0bf7fe021deeea76b3ab334d2c38218", size = 61689, upload-time = "2025-11-07T00:44:06.719Z" }, + { url = "https://files.pythonhosted.org/packages/cf/67/d7a7c276d874e5d26738c22444d466a3a64ed541f6ef35f740dbd865bab4/wrapt-2.0.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8d60527d1ecfc131426b10d93ab5d53e08a09c5fa0175f6b21b3252080c70a9", size = 121502, upload-time = "2025-11-07T00:44:09.557Z" }, + { url = "https://files.pythonhosted.org/packages/0f/6b/806dbf6dd9579556aab22fc92908a876636e250f063f71548a8660382184/wrapt-2.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c654eafb01afac55246053d67a4b9a984a3567c3808bb7df2f8de1c1caba2e1c", size = 123110, upload-time = "2025-11-07T00:44:10.64Z" }, + { url = "https://files.pythonhosted.org/packages/e5/08/cdbb965fbe4c02c5233d185d070cabed2ecc1f1e47662854f95d77613f57/wrapt-2.0.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:98d873ed6c8b4ee2418f7afce666751854d6d03e3c0ec2a399bb039cd2ae89db", size = 117434, upload-time = "2025-11-07T00:44:08.138Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d1/6aae2ce39db4cb5216302fa2e9577ad74424dfbe315bd6669725569e048c/wrapt-2.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9e850f5b7fc67af856ff054c71690d54fa940c3ef74209ad9f935b4f66a0233", size = 121533, upload-time = "2025-11-07T00:44:12.142Z" }, + { url = "https://files.pythonhosted.org/packages/79/35/565abf57559fbe0a9155c29879ff43ce8bd28d2ca61033a3a3dd67b70794/wrapt-2.0.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e505629359cb5f751e16e30cf3f91a1d3ddb4552480c205947da415d597f7ac2", size = 116324, upload-time = "2025-11-07T00:44:13.28Z" }, + { url = "https://files.pythonhosted.org/packages/e1/e0/53ff5e76587822ee33e560ad55876d858e384158272cd9947abdd4ad42ca/wrapt-2.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2879af909312d0baf35f08edeea918ee3af7ab57c37fe47cb6a373c9f2749c7b", size = 120627, upload-time = "2025-11-07T00:44:14.431Z" }, + { url = "https://files.pythonhosted.org/packages/7c/7b/38df30fd629fbd7612c407643c63e80e1c60bcc982e30ceeae163a9800e7/wrapt-2.0.1-cp313-cp313-win32.whl", hash = "sha256:d67956c676be5a24102c7407a71f4126d30de2a569a1c7871c9f3cabc94225d7", size = 58252, upload-time = "2025-11-07T00:44:17.814Z" }, + { url = "https://files.pythonhosted.org/packages/85/64/d3954e836ea67c4d3ad5285e5c8fd9d362fd0a189a2db622df457b0f4f6a/wrapt-2.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:9ca66b38dd642bf90c59b6738af8070747b610115a39af2498535f62b5cdc1c3", size = 60500, upload-time = "2025-11-07T00:44:15.561Z" }, + { url = "https://files.pythonhosted.org/packages/89/4e/3c8b99ac93527cfab7f116089db120fef16aac96e5f6cdb724ddf286086d/wrapt-2.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:5a4939eae35db6b6cec8e7aa0e833dcca0acad8231672c26c2a9ab7a0f8ac9c8", size = 58993, upload-time = "2025-11-07T00:44:16.65Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f4/eff2b7d711cae20d220780b9300faa05558660afb93f2ff5db61fe725b9a/wrapt-2.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a52f93d95c8d38fed0669da2ebdb0b0376e895d84596a976c15a9eb45e3eccb3", size = 82028, upload-time = "2025-11-07T00:44:18.944Z" }, + { url = "https://files.pythonhosted.org/packages/0c/67/cb945563f66fd0f61a999339460d950f4735c69f18f0a87ca586319b1778/wrapt-2.0.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e54bbf554ee29fcceee24fa41c4d091398b911da6e7f5d7bffda963c9aed2e1", size = 62949, upload-time = "2025-11-07T00:44:20.074Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ca/f63e177f0bbe1e5cf5e8d9b74a286537cd709724384ff20860f8f6065904/wrapt-2.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:908f8c6c71557f4deaa280f55d0728c3bca0960e8c3dd5ceeeafb3c19942719d", size = 63681, upload-time = "2025-11-07T00:44:21.345Z" }, + { url = "https://files.pythonhosted.org/packages/39/a1/1b88fcd21fd835dca48b556daef750952e917a2794fa20c025489e2e1f0f/wrapt-2.0.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e2f84e9af2060e3904a32cea9bb6db23ce3f91cfd90c6b426757cf7cc01c45c7", size = 152696, upload-time = "2025-11-07T00:44:24.318Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/d9185500c1960d9f5f77b9c0b890b7fc62282b53af7ad1b6bd779157f714/wrapt-2.0.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3612dc06b436968dfb9142c62e5dfa9eb5924f91120b3c8ff501ad878f90eb3", size = 158859, upload-time = "2025-11-07T00:44:25.494Z" }, + { url = "https://files.pythonhosted.org/packages/91/60/5d796ed0f481ec003220c7878a1d6894652efe089853a208ea0838c13086/wrapt-2.0.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d2d947d266d99a1477cd005b23cbd09465276e302515e122df56bb9511aca1b", size = 146068, upload-time = "2025-11-07T00:44:22.81Z" }, + { url = "https://files.pythonhosted.org/packages/04/f8/75282dd72f102ddbfba137e1e15ecba47b40acff32c08ae97edbf53f469e/wrapt-2.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7d539241e87b650cbc4c3ac9f32c8d1ac8a54e510f6dca3f6ab60dcfd48c9b10", size = 155724, upload-time = "2025-11-07T00:44:26.634Z" }, + { url = "https://files.pythonhosted.org/packages/5a/27/fe39c51d1b344caebb4a6a9372157bdb8d25b194b3561b52c8ffc40ac7d1/wrapt-2.0.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:4811e15d88ee62dbf5c77f2c3ff3932b1e3ac92323ba3912f51fc4016ce81ecf", size = 144413, upload-time = "2025-11-07T00:44:27.939Z" }, + { url = "https://files.pythonhosted.org/packages/83/2b/9f6b643fe39d4505c7bf926d7c2595b7cb4b607c8c6b500e56c6b36ac238/wrapt-2.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c1c91405fcf1d501fa5d55df21e58ea49e6b879ae829f1039faaf7e5e509b41e", size = 150325, upload-time = "2025-11-07T00:44:29.29Z" }, + { url = "https://files.pythonhosted.org/packages/bb/b6/20ffcf2558596a7f58a2e69c89597128781f0b88e124bf5a4cadc05b8139/wrapt-2.0.1-cp313-cp313t-win32.whl", hash = "sha256:e76e3f91f864e89db8b8d2a8311d57df93f01ad6bb1e9b9976d1f2e83e18315c", size = 59943, upload-time = "2025-11-07T00:44:33.211Z" }, + { url = "https://files.pythonhosted.org/packages/87/6a/0e56111cbb3320151eed5d3821ee1373be13e05b376ea0870711f18810c3/wrapt-2.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:83ce30937f0ba0d28818807b303a412440c4b63e39d3d8fc036a94764b728c92", size = 63240, upload-time = "2025-11-07T00:44:30.935Z" }, + { url = "https://files.pythonhosted.org/packages/1d/54/5ab4c53ea1f7f7e5c3e7c1095db92932cc32fd62359d285486d00c2884c3/wrapt-2.0.1-cp313-cp313t-win_arm64.whl", hash = "sha256:4b55cacc57e1dc2d0991dbe74c6419ffd415fb66474a02335cb10efd1aa3f84f", size = 60416, upload-time = "2025-11-07T00:44:32.002Z" }, + { url = "https://files.pythonhosted.org/packages/73/81/d08d83c102709258e7730d3cd25befd114c60e43ef3891d7e6877971c514/wrapt-2.0.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:5e53b428f65ece6d9dad23cb87e64506392b720a0b45076c05354d27a13351a1", size = 78290, upload-time = "2025-11-07T00:44:34.691Z" }, + { url = "https://files.pythonhosted.org/packages/f6/14/393afba2abb65677f313aa680ff0981e829626fed39b6a7e3ec807487790/wrapt-2.0.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ad3ee9d0f254851c71780966eb417ef8e72117155cff04821ab9b60549694a55", size = 61255, upload-time = "2025-11-07T00:44:35.762Z" }, + { url = "https://files.pythonhosted.org/packages/c4/10/a4a1f2fba205a9462e36e708ba37e5ac95f4987a0f1f8fd23f0bf1fc3b0f/wrapt-2.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7b822c61ed04ee6ad64bc90d13368ad6eb094db54883b5dde2182f67a7f22c0", size = 61797, upload-time = "2025-11-07T00:44:37.22Z" }, + { url = "https://files.pythonhosted.org/packages/12/db/99ba5c37cf1c4fad35349174f1e38bd8d992340afc1ff27f526729b98986/wrapt-2.0.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7164a55f5e83a9a0b031d3ffab4d4e36bbec42e7025db560f225489fa929e509", size = 120470, upload-time = "2025-11-07T00:44:39.425Z" }, + { url = "https://files.pythonhosted.org/packages/30/3f/a1c8d2411eb826d695fc3395a431757331582907a0ec59afce8fe8712473/wrapt-2.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e60690ba71a57424c8d9ff28f8d006b7ad7772c22a4af432188572cd7fa004a1", size = 122851, upload-time = "2025-11-07T00:44:40.582Z" }, + { url = "https://files.pythonhosted.org/packages/b3/8d/72c74a63f201768d6a04a8845c7976f86be6f5ff4d74996c272cefc8dafc/wrapt-2.0.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3cd1a4bd9a7a619922a8557e1318232e7269b5fb69d4ba97b04d20450a6bf970", size = 117433, upload-time = "2025-11-07T00:44:38.313Z" }, + { url = "https://files.pythonhosted.org/packages/c7/5a/df37cf4042cb13b08256f8e27023e2f9b3d471d553376616591bb99bcb31/wrapt-2.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b4c2e3d777e38e913b8ce3a6257af72fb608f86a1df471cb1d4339755d0a807c", size = 121280, upload-time = "2025-11-07T00:44:41.69Z" }, + { url = "https://files.pythonhosted.org/packages/54/34/40d6bc89349f9931e1186ceb3e5fbd61d307fef814f09fbbac98ada6a0c8/wrapt-2.0.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3d366aa598d69416b5afedf1faa539fac40c1d80a42f6b236c88c73a3c8f2d41", size = 116343, upload-time = "2025-11-07T00:44:43.013Z" }, + { url = "https://files.pythonhosted.org/packages/70/66/81c3461adece09d20781dee17c2366fdf0cb8754738b521d221ca056d596/wrapt-2.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c235095d6d090aa903f1db61f892fffb779c1eaeb2a50e566b52001f7a0f66ed", size = 119650, upload-time = "2025-11-07T00:44:44.523Z" }, + { url = "https://files.pythonhosted.org/packages/46/3a/d0146db8be8761a9e388cc9cc1c312b36d583950ec91696f19bbbb44af5a/wrapt-2.0.1-cp314-cp314-win32.whl", hash = "sha256:bfb5539005259f8127ea9c885bdc231978c06b7a980e63a8a61c8c4c979719d0", size = 58701, upload-time = "2025-11-07T00:44:48.277Z" }, + { url = "https://files.pythonhosted.org/packages/1a/38/5359da9af7d64554be63e9046164bd4d8ff289a2dd365677d25ba3342c08/wrapt-2.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:4ae879acc449caa9ed43fc36ba08392b9412ee67941748d31d94e3cedb36628c", size = 60947, upload-time = "2025-11-07T00:44:46.086Z" }, + { url = "https://files.pythonhosted.org/packages/aa/3f/96db0619276a833842bf36343685fa04f987dd6e3037f314531a1e00492b/wrapt-2.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:8639b843c9efd84675f1e100ed9e99538ebea7297b62c4b45a7042edb84db03e", size = 59359, upload-time = "2025-11-07T00:44:47.164Z" }, + { url = "https://files.pythonhosted.org/packages/71/49/5f5d1e867bf2064bf3933bc6cf36ade23505f3902390e175e392173d36a2/wrapt-2.0.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:9219a1d946a9b32bb23ccae66bdb61e35c62773ce7ca6509ceea70f344656b7b", size = 82031, upload-time = "2025-11-07T00:44:49.4Z" }, + { url = "https://files.pythonhosted.org/packages/2b/89/0009a218d88db66ceb83921e5685e820e2c61b59bbbb1324ba65342668bc/wrapt-2.0.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fa4184e74197af3adad3c889a1af95b53bb0466bced92ea99a0c014e48323eec", size = 62952, upload-time = "2025-11-07T00:44:50.74Z" }, + { url = "https://files.pythonhosted.org/packages/ae/18/9b968e920dd05d6e44bcc918a046d02afea0fb31b2f1c80ee4020f377cbe/wrapt-2.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c5ef2f2b8a53b7caee2f797ef166a390fef73979b15778a4a153e4b5fedce8fa", size = 63688, upload-time = "2025-11-07T00:44:52.248Z" }, + { url = "https://files.pythonhosted.org/packages/a6/7d/78bdcb75826725885d9ea26c49a03071b10c4c92da93edda612910f150e4/wrapt-2.0.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e042d653a4745be832d5aa190ff80ee4f02c34b21f4b785745eceacd0907b815", size = 152706, upload-time = "2025-11-07T00:44:54.613Z" }, + { url = "https://files.pythonhosted.org/packages/dd/77/cac1d46f47d32084a703df0d2d29d47e7eb2a7d19fa5cbca0e529ef57659/wrapt-2.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2afa23318136709c4b23d87d543b425c399887b4057936cd20386d5b1422b6fa", size = 158866, upload-time = "2025-11-07T00:44:55.79Z" }, + { url = "https://files.pythonhosted.org/packages/8a/11/b521406daa2421508903bf8d5e8b929216ec2af04839db31c0a2c525eee0/wrapt-2.0.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c72328f668cf4c503ffcf9434c2b71fdd624345ced7941bc6693e61bbe36bef", size = 146148, upload-time = "2025-11-07T00:44:53.388Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c0/340b272bed297baa7c9ce0c98ef7017d9c035a17a6a71dce3184b8382da2/wrapt-2.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3793ac154afb0e5b45d1233cb94d354ef7a983708cc3bb12563853b1d8d53747", size = 155737, upload-time = "2025-11-07T00:44:56.971Z" }, + { url = "https://files.pythonhosted.org/packages/f3/93/bfcb1fb2bdf186e9c2883a4d1ab45ab099c79cbf8f4e70ea453811fa3ea7/wrapt-2.0.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fec0d993ecba3991645b4857837277469c8cc4c554a7e24d064d1ca291cfb81f", size = 144451, upload-time = "2025-11-07T00:44:58.515Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6b/dca504fb18d971139d232652656180e3bd57120e1193d9a5899c3c0b7cdd/wrapt-2.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:949520bccc1fa227274da7d03bf238be15389cd94e32e4297b92337df9b7a349", size = 150353, upload-time = "2025-11-07T00:44:59.753Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f6/a1de4bd3653afdf91d250ca5c721ee51195df2b61a4603d4b373aa804d1d/wrapt-2.0.1-cp314-cp314t-win32.whl", hash = "sha256:be9e84e91d6497ba62594158d3d31ec0486c60055c49179edc51ee43d095f79c", size = 60609, upload-time = "2025-11-07T00:45:03.315Z" }, + { url = "https://files.pythonhosted.org/packages/01/3a/07cd60a9d26fe73efead61c7830af975dfdba8537632d410462672e4432b/wrapt-2.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:61c4956171c7434634401db448371277d07032a81cc21c599c22953374781395", size = 64038, upload-time = "2025-11-07T00:45:00.948Z" }, + { url = "https://files.pythonhosted.org/packages/41/99/8a06b8e17dddbf321325ae4eb12465804120f699cd1b8a355718300c62da/wrapt-2.0.1-cp314-cp314t-win_arm64.whl", hash = "sha256:35cdbd478607036fee40273be8ed54a451f5f23121bd9d4be515158f9498f7ad", size = 60634, upload-time = "2025-11-07T00:45:02.087Z" }, + { url = "https://files.pythonhosted.org/packages/15/d1/b51471c11592ff9c012bd3e2f7334a6ff2f42a7aed2caffcf0bdddc9cb89/wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca", size = 44046, upload-time = "2025-11-07T00:45:32.116Z" }, ] From 5e69941e15f38488ba21349e8551c887de26b7d3 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 27 Nov 2025 10:27:28 +0000 Subject: [PATCH 56/79] feat(backend): Allow patching videos --- backend/app/api/data_collection/routers.py | 23 ++++++++++++++++++-- backend/app/api/file_storage/crud.py | 5 +++-- backend/app/api/file_storage/schemas.py | 11 +++++++--- frontend-app/src/app/products/[id]/index.tsx | 2 +- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/backend/app/api/data_collection/routers.py b/backend/app/api/data_collection/routers.py index 58b5e52..475a24b 100644 --- a/backend/app/api/data_collection/routers.py +++ b/backend/app/api/data_collection/routers.py @@ -65,11 +65,11 @@ ProductUpdate, ProductUpdateWithProperties, ) -from app.api.file_storage.crud import create_video, delete_video +from app.api.file_storage.crud import create_video, delete_video, update_video from app.api.file_storage.filters import VideoFilter from app.api.file_storage.models.models import Video from app.api.file_storage.router_factories import StorageRouteMethod, add_storage_routes -from app.api.file_storage.schemas import VideoCreateWithinProduct, VideoReadWithinProduct +from app.api.file_storage.schemas import VideoCreateWithinProduct, VideoReadWithinProduct, VideoUpdateWithinProduct if TYPE_CHECKING: from sqlmodel.sql._expression_select_cls import SelectOfScalar @@ -944,6 +944,25 @@ async def create_product_video( return await create_video(session, video, product_id=product.id) +@product_router.patch( + "/{product_id}/videos/{video_id}", + response_model=VideoReadWithinProduct, + summary="Update video by ID", +) +async def update_product_video( + product: UserOwnedProductDep, + video_id: PositiveInt, + video_update: VideoUpdateWithinProduct, + session: AsyncSessionDep, +) -> Video: + """Update a video associated with a specific product.""" + # Validate existence of product and video + await get_nested_model_by_id(session, Product, product.id, Video, video_id, "product_id") + + # Update video + return await update_video(session, video_id, video_update) + + @product_router.delete( "/{product_id}/videos/{video_id}", status_code=204, diff --git a/backend/app/api/file_storage/crud.py b/backend/app/api/file_storage/crud.py index d07bcdb..775cd82 100644 --- a/backend/app/api/file_storage/crud.py +++ b/backend/app/api/file_storage/crud.py @@ -4,7 +4,7 @@ import uuid from collections.abc import Callable, Sequence from pathlib import Path -from typing import Any, Generic, TypeVar +from typing import Any, TypeVar from anyio import to_thread from fastapi import UploadFile @@ -30,6 +30,7 @@ VideoCreate, VideoCreateWithinProduct, VideoUpdate, + VideoUpdateWithinProduct, ) logger = logging.getLogger(__name__) @@ -269,7 +270,7 @@ async def create_video( return db_video -async def update_video(db: AsyncSession, video_id: int, video: VideoUpdate) -> Video: +async def update_video(db: AsyncSession, video_id: int, video: VideoUpdate | VideoUpdateWithinProduct) -> Video: """Update an existing video in the database.""" db_video: Video = await db_get_model_with_id_if_it_exists(db, Video, video_id) diff --git a/backend/app/api/file_storage/schemas.py b/backend/app/api/file_storage/schemas.py index ed737a0..23b22be 100644 --- a/backend/app/api/file_storage/schemas.py +++ b/backend/app/api/file_storage/schemas.py @@ -185,7 +185,7 @@ class VideoCreate(BaseCreateSchema, VideoBase): class VideoReadWithinProduct(BaseReadSchemaWithTimeStamp, VideoBase): - """Schema for reading video information.""" + """Schema for reading video information within a product.""" class VideoRead(BaseReadSchemaWithTimeStamp, VideoBase): @@ -194,11 +194,16 @@ class VideoRead(BaseReadSchemaWithTimeStamp, VideoBase): product_id: PositiveInt -class VideoUpdate(BaseUpdateSchema): - """Schema for updating a video.""" +class VideoUpdateWithinProduct(BaseUpdateSchema): + """Schema for updating a video within a product.""" url: AnyUrlToDB | None = Field(default=None, description="URL linking to the video") title: str | None = Field(default=None, max_length=100, description="Title of the video") description: str | None = Field(default=None, max_length=500, description="Description of the video") video_metadata: dict[str, Any] | None = Field(default=None, description="Video metadata as a JSON dict") + + +class VideoUpdate(VideoUpdateWithinProduct): + """Schema for updating a video.""" + product_id: PositiveInt diff --git a/frontend-app/src/app/products/[id]/index.tsx b/frontend-app/src/app/products/[id]/index.tsx index 3c59cce..6f16f8d 100644 --- a/frontend-app/src/app/products/[id]/index.tsx +++ b/frontend-app/src/app/products/[id]/index.tsx @@ -7,6 +7,7 @@ import { KeyboardAwareScrollView } from 'react-native-keyboard-controller'; import { AnimatedFAB, Button, Tooltip, useTheme } from 'react-native-paper'; import ProductAmountInParent from '@/components/product/ProductAmountInParent'; +import ProductCircularityProperties from '@/components/product/ProductCircularityProperties'; import ProductComponents from '@/components/product/ProductComponents'; import ProductDelete from '@/components/product/ProductDelete'; import ProductDescription from '@/components/product/ProductDescription'; @@ -15,7 +16,6 @@ import ProductMetaData from '@/components/product/ProductMetaData'; import ProductPhysicalProperties from '@/components/product/ProductPhysicalProperties'; import ProductTags from '@/components/product/ProductTags'; import ProductType from '@/components/product/ProductType'; -import ProductCircularityProperties from '@/components/product/ProductCircularityProperties'; import ProductVideo from "@/components/product/ProductVideo"; import { useDialog } from '@/components/common/DialogProvider'; From 93a469ed3849e4deda85903c767d82972cdd703b Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 27 Nov 2025 10:32:07 +0000 Subject: [PATCH 57/79] Merge pull request #80 from CMLPlatform/frontend-app-circularity-properties Frontend app circularity properties --- frontend-app/src/app/products/[id]/index.tsx | 1 + .../product/ProductCircularityProperties.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend-app/src/app/products/[id]/index.tsx b/frontend-app/src/app/products/[id]/index.tsx index 6f16f8d..94fdf41 100644 --- a/frontend-app/src/app/products/[id]/index.tsx +++ b/frontend-app/src/app/products/[id]/index.tsx @@ -16,6 +16,7 @@ import ProductMetaData from '@/components/product/ProductMetaData'; import ProductPhysicalProperties from '@/components/product/ProductPhysicalProperties'; import ProductTags from '@/components/product/ProductTags'; import ProductType from '@/components/product/ProductType'; +import ProductCircularityProperties from '@/components/product/ProductCircularityProperties'; import ProductVideo from "@/components/product/ProductVideo"; import { useDialog } from '@/components/common/DialogProvider'; diff --git a/frontend-app/src/components/product/ProductCircularityProperties.tsx b/frontend-app/src/components/product/ProductCircularityProperties.tsx index acfe890..13dce1d 100644 --- a/frontend-app/src/components/product/ProductCircularityProperties.tsx +++ b/frontend-app/src/components/product/ProductCircularityProperties.tsx @@ -1,10 +1,10 @@ -import { MaterialCommunityIcons } from '@expo/vector-icons'; -import { Fragment, useState } from 'react'; -import { View, StyleSheet, Pressable, useColorScheme } from 'react-native'; -import { Chip, InfoTooltip, Text, TextInput } from '@/components/base'; -import { CircularityProperties, Product } from '@/types/Product'; import DarkTheme from '@/assets/themes/dark'; import LightTheme from '@/assets/themes/light'; +import { Chip, InfoTooltip, Text, TextInput } from '@/components/base'; +import { CircularityProperties, Product } from '@/types/Product'; +import { MaterialCommunityIcons } from '@expo/vector-icons'; +import { Fragment, useState } from 'react'; +import { Pressable, StyleSheet, useColorScheme, View } from 'react-native'; interface Props { product: Product; From 89c64ce7cbbb9685cfa73a8072a66d101fd8a056 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 27 Nov 2025 10:33:59 +0000 Subject: [PATCH 58/79] chore: linting --- .pre-commit-config.yaml | 4 +- backend/tests/conftest.py | 2 +- frontend-app/src/app/products/[id]/index.tsx | 3 +- .../product/ProductCircularityProperties.tsx | 255 +++++++++--------- .../src/components/product/ProductVideo.tsx | 252 ++++++++--------- frontend-app/src/services/api/saving.ts | 9 +- frontend-app/src/types/Product.ts | 3 +- 7 files changed, 267 insertions(+), 261 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ce5a11e..898da1b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: - id: pre-commit-update # Autoupdate pre-commit hooks - repo: https://github.com/gitleaks/gitleaks - rev: v8.29.1 + rev: v8.30.0 hooks: - id: gitleaks @@ -72,7 +72,7 @@ repos: args: ["--config", "backend/pyproject.toml"] - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.11 + rev: 0.9.13 hooks: - id: uv-lock # Update the uv lockfile for the backend. files: ^backend/(uv\.lock|pyproject\.toml|uv\.toml)$ diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 843680c..5deafde 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -9,6 +9,7 @@ from unittest.mock import AsyncMock import pytest +from alembic.config import Config from fastapi.testclient import TestClient from sqlalchemy import Engine, create_engine, text from sqlalchemy.exc import ProgrammingError @@ -17,7 +18,6 @@ from sqlmodel.ext.asyncio.session import AsyncSession from alembic import command -from alembic.config import Config from app.core.config import settings from app.main import app diff --git a/frontend-app/src/app/products/[id]/index.tsx b/frontend-app/src/app/products/[id]/index.tsx index 94fdf41..611235e 100644 --- a/frontend-app/src/app/products/[id]/index.tsx +++ b/frontend-app/src/app/products/[id]/index.tsx @@ -16,8 +16,7 @@ import ProductMetaData from '@/components/product/ProductMetaData'; import ProductPhysicalProperties from '@/components/product/ProductPhysicalProperties'; import ProductTags from '@/components/product/ProductTags'; import ProductType from '@/components/product/ProductType'; -import ProductCircularityProperties from '@/components/product/ProductCircularityProperties'; -import ProductVideo from "@/components/product/ProductVideo"; +import ProductVideo from '@/components/product/ProductVideo'; import { useDialog } from '@/components/common/DialogProvider'; diff --git a/frontend-app/src/components/product/ProductCircularityProperties.tsx b/frontend-app/src/components/product/ProductCircularityProperties.tsx index 13dce1d..3166d81 100644 --- a/frontend-app/src/components/product/ProductCircularityProperties.tsx +++ b/frontend-app/src/components/product/ProductCircularityProperties.tsx @@ -31,11 +31,7 @@ interface CircularityPropertySectionProps { onUpdateField: (field: 'comment' | 'observation' | 'reference', value: string) => void; } -export default function ProductCircularityProperties({ - product, - editMode, - onChangeCircularityProperties, -}: Props) { +export default function ProductCircularityProperties({ product, editMode, onChangeCircularityProperties }: Props) { const darkMode = useColorScheme() === 'dark'; const theme = darkMode ? DarkTheme : LightTheme; const [expandedProperty, setExpandedProperty] = useState(null); @@ -66,12 +62,17 @@ export default function ProductCircularityProperties({ (typeof reference === 'string' && reference.trim() !== '') || // Also consider it as "having data" if comment or reference are empty strings (not null) // This means the property was added but not yet filled in - (comment !== null || reference !== null) + comment !== null || + reference !== null ); }; // Helper function to update a specific field - const updateField = (type: CircularityPropertyType, field: 'comment' | 'observation' | 'reference', value: string) => { + const updateField = ( + type: CircularityPropertyType, + field: 'comment' | 'observation' | 'reference', + value: string, + ) => { if (!product.circularityProperties) return; const key = `${type}${field.charAt(0).toUpperCase()}${field.slice(1)}` as keyof CircularityProperties; @@ -121,8 +122,6 @@ export default function ProductCircularityProperties({ setExpandedProperty(null); }; - - const propertyTypes = ['recyclability', 'remanufacturability', 'repairability'] as CircularityPropertyType[]; const chipsToShow = editMode ? propertyTypes.filter((type) => !hasPropertyData(type)) : []; const expandedPropertiesToShow = propertyTypes.filter((type) => hasPropertyData(type)); @@ -157,134 +156,135 @@ export default function ProductCircularityProperties({ )} {/* Render expanded properties */} - {expandedPropertiesToShow.map((type) => ( - product.circularityProperties && ( - setExpandedProperty(expandedProperty === type ? null : type)} - onRemove={() => removeProperty(type)} - onUpdateField={(field, value) => updateField(type, field, value)} - /> - ) - ))} + {expandedPropertiesToShow.map( + (type) => + product.circularityProperties && ( + setExpandedProperty(expandedProperty === type ? null : type)} + onRemove={() => removeProperty(type)} + onUpdateField={(field, value) => updateField(type, field, value)} + /> + ), + )}
); } function CircularityPropertySection({ - type, - circularityProperties, - editMode, - isExpanded, - onToggleExpanded, - onRemove, - onUpdateField, - }: CircularityPropertySectionProps) { - const darkMode = useColorScheme() === 'dark'; - const theme = darkMode ? DarkTheme : LightTheme; - - const commentKey = `${type}Comment` as keyof CircularityProperties; - const observationKey = `${type}Observation` as keyof CircularityProperties; - const referenceKey = `${type}Reference` as keyof CircularityProperties; - - // Helper to check if a field has content - const hasContent = (value: string | null | undefined): boolean => { - return typeof value === 'string' && value.trim() !== ''; - }; + type, + circularityProperties, + editMode, + isExpanded, + onToggleExpanded, + onRemove, + onUpdateField, +}: CircularityPropertySectionProps) { + const darkMode = useColorScheme() === 'dark'; + const theme = darkMode ? DarkTheme : LightTheme; - const observation = circularityProperties[observationKey]; - const comment = circularityProperties[commentKey]; - const reference = circularityProperties[referenceKey]; + const commentKey = `${type}Comment` as keyof CircularityProperties; + const observationKey = `${type}Observation` as keyof CircularityProperties; + const referenceKey = `${type}Reference` as keyof CircularityProperties; - return ( - - - - - {propertyLabels[type]} - - [styles.iconButton, pressed && styles.iconButtonPressed]} - > - - - {editMode && ( - [styles.iconButton, pressed && styles.iconButtonPressed]} - > - - - )} - - + // Helper to check if a field has content + const hasContent = (value: string | null | undefined): boolean => { + return typeof value === 'string' && value.trim() !== ''; + }; - {isExpanded && ( - - {/* Observation field - always show in edit mode, only show if has content in view mode */} - {(editMode || hasContent(observation)) && ( - - Observation (Required) - onUpdateField('observation', text)} - multiline - numberOfLines={3} - editable={editMode} - style={[ - styles.input, - styles.multilineInput, - darkMode && styles.inputDark, - editMode && !observation && styles.inputError, - editMode && !observation && darkMode && styles.inputErrorDark, - ]} - errorOnEmpty={editMode && !observation} - /> - - )} + const observation = circularityProperties[observationKey]; + const comment = circularityProperties[commentKey]; + const reference = circularityProperties[referenceKey]; - {/* Comment field - always show in edit mode, only show if has content in view mode */} - {(editMode || hasContent(comment)) && ( - - Comment (Optional) - onUpdateField('comment', text)} - multiline - numberOfLines={2} - editable={editMode} - style={[styles.input, styles.multilineInput, darkMode && styles.inputDark]} - /> - - )} + return ( + + + + + {propertyLabels[type]} + + [styles.iconButton, pressed && styles.iconButtonPressed]} + > + + + {editMode && ( + [styles.iconButton, pressed && styles.iconButtonPressed]} + > + + + )} + + - {/* Reference field - always show in edit mode, only show if has content in view mode */} - {(editMode || hasContent(reference)) && ( - - Reference (Optional) - onUpdateField('reference', text)} - editable={editMode} - style={[styles.input, darkMode && styles.inputDark]} - placeholder="e.g., ISO 14021:2016" - /> - - )} - - )} - - - ); + {isExpanded && ( + + {/* Observation field - always show in edit mode, only show if has content in view mode */} + {(editMode || hasContent(observation)) && ( + + Observation (Required) + onUpdateField('observation', text)} + multiline + numberOfLines={3} + editable={editMode} + style={[ + styles.input, + styles.multilineInput, + darkMode && styles.inputDark, + editMode && !observation && styles.inputError, + editMode && !observation && darkMode && styles.inputErrorDark, + ]} + errorOnEmpty={editMode && !observation} + /> + + )} + + {/* Comment field - always show in edit mode, only show if has content in view mode */} + {(editMode || hasContent(comment)) && ( + + Comment (Optional) + onUpdateField('comment', text)} + multiline + numberOfLines={2} + editable={editMode} + style={[styles.input, styles.multilineInput, darkMode && styles.inputDark]} + /> + + )} + + {/* Reference field - always show in edit mode, only show if has content in view mode */} + {(editMode || hasContent(reference)) && ( + + Reference (Optional) + onUpdateField('reference', text)} + editable={editMode} + style={[styles.input, darkMode && styles.inputDark]} + placeholder="e.g., ISO 14021:2016" + /> + + )} + + )} + + + ); } const styles = StyleSheet.create({ @@ -372,4 +372,3 @@ const styles = StyleSheet.create({ textAlignVertical: 'top', }, }); - diff --git a/frontend-app/src/components/product/ProductVideo.tsx b/frontend-app/src/components/product/ProductVideo.tsx index 3c378a1..0405437 100644 --- a/frontend-app/src/components/product/ProductVideo.tsx +++ b/frontend-app/src/components/product/ProductVideo.tsx @@ -1,141 +1,153 @@ -import { useState } from 'react'; -import { View, Text, TouchableOpacity, Linking } from 'react-native'; -import { MaterialCommunityIcons } from '@expo/vector-icons'; import { InfoTooltip, TextInput } from '@/components/base'; import { useDialog } from '@/components/common/DialogProvider'; -import { Product } from '@/types/Product'; import { isValidUrl } from '@/services/api/validation/product'; +import { Product } from '@/types/Product'; +import { MaterialCommunityIcons } from '@expo/vector-icons'; +import { useState } from 'react'; +import { Linking, Text, TouchableOpacity, View } from 'react-native'; interface Video { - id?: number; - url: string; - title: string; - description: string; + id?: number; + url: string; + title: string; + description: string; } interface Props { - product: Product; - editMode: boolean; - onVideoChange?: (videos: Video[]) => void; + product: Product; + editMode: boolean; + onVideoChange?: (videos: Video[]) => void; } export default function ProductVideo({ product, editMode, onVideoChange }: Props) { - const [videos, setVideos] = useState(product.videos || []); - const dialog = useDialog(); + const [videos, setVideos] = useState(product.videos || []); + const dialog = useDialog(); - const handleVideoChange = (idx: number, field: 'url' | 'title' | 'description', value: string) => { - const updated = videos.map((v, i) => i === idx ? { ...v, [field]: value } : v); - setVideos(updated); - onVideoChange?.(updated); - }; + const handleVideoChange = (idx: number, field: 'url' | 'title' | 'description', value: string) => { + const updated = videos.map((v, i) => (i === idx ? { ...v, [field]: value } : v)); + setVideos(updated); + onVideoChange?.(updated); + }; - const handleRemove = (idx: number) => { - const updated = videos.filter((_, i) => i !== idx); - setVideos(updated); - onVideoChange?.(updated); - }; + const handleRemove = (idx: number) => { + const updated = videos.filter((_, i) => i !== idx); + setVideos(updated); + onVideoChange?.(updated); + }; - const handleAdd = () => { - dialog.input({ - title: 'Add Recording', - placeholder: 'Video URL', - helperText: 'Paste a video URL (YouTube)', - buttons: [ - { text: 'Cancel' }, - { - text: 'Add', - disabled: (value) => !value || !value.trim() || !isValidUrl(value), - onPress: (url) => { - if (!url || !isValidUrl(url)) return; - const updated = [...videos, { url: url.trim(), title: '', description: '' }]; - setVideos(updated); - onVideoChange?.(updated); - } - } - ] - }); - }; + const handleAdd = () => { + dialog.input({ + title: 'Add Recording', + placeholder: 'Video URL', + helperText: 'Paste a video URL (YouTube)', + buttons: [ + { text: 'Cancel' }, + { + text: 'Add', + disabled: (value) => !value || !value.trim() || !isValidUrl(value), + onPress: (url) => { + if (!url || !isValidUrl(url)) return; + const updated = [...videos, { url: url.trim(), title: '', description: '' }]; + setVideos(updated); + onVideoChange?.(updated); + }, + }, + ], + }); + }; - return ( - - + return ( + + + + Recordings + + {editMode && ( + + Add recording + + )} + + + {videos.length === 0 && ( + + This product has no associated recordings. + + )} + + {videos.map((video, idx) => ( + + + handleVideoChange(idx, 'title', val)} + editable={editMode} + errorOnEmpty + /> + {editMode ? ( + handleVideoChange(idx, 'url', val)} + errorOnEmpty + customValidation={isValidUrl} + editable={editMode} + /> + ) : ( + Linking.openURL(video.url)}> - Recordings + {video.url} - {editMode && ( - - Add recording - - )} - - - {videos.length === 0 && ( - This product has no associated recordings. + )} - - {videos.map((video, idx) => ( - - - handleVideoChange(idx, 'title', val)} - editable={editMode} - errorOnEmpty - /> - {editMode ? ( - handleVideoChange(idx, 'url', val)} - errorOnEmpty - customValidation={isValidUrl} - editable={editMode} - /> - ) : ( - Linking.openURL(video.url)}> - - {video.url} - - - )} - {(editMode || Boolean(video.description)) && handleVideoChange(idx, 'description', val)} - editable={editMode} - />} - - {editMode && ( - handleRemove(idx)} - style={{ - padding: 14, - justifyContent: 'center', - alignItems: 'center' - }} - > - - - )} - - ))} + {(editMode || Boolean(video.description)) && ( + handleVideoChange(idx, 'description', val)} + editable={editMode} + /> + )} + + {editMode && ( + handleRemove(idx)} + style={{ + padding: 14, + justifyContent: 'center', + alignItems: 'center', + }} + > + + + )} - ); + ))} + + ); } diff --git a/frontend-app/src/services/api/saving.ts b/frontend-app/src/services/api/saving.ts index 46ac57c..c422a4f 100644 --- a/frontend-app/src/services/api/saving.ts +++ b/frontend-app/src/services/api/saving.ts @@ -200,10 +200,7 @@ async function updateProductVideos(product: Product) { const videosToAdd = product.videos.filter((vid) => !vid.id); const videosToUpdate = product.videos.filter((vid) => { const orig = currentVideos.find((v) => v.id === vid.id); - return orig && (orig.url !== vid.url - || orig.description !== vid.description - || orig.title !== vid.title - ); + return orig && (orig.url !== vid.url || orig.description !== vid.description || orig.title !== vid.title); }); for (const vid of videosToDelete) { @@ -217,7 +214,7 @@ async function updateProductVideos(product: Product) { } } -async function addVideo(product: Product, video: { url: string; description: string, title: string, }) { +async function addVideo(product: Product, video: { url: string; description: string; title: string }) { const url = new URL(baseUrl + `/products/${product.id}/videos`); const token = await getToken(); const headers = { @@ -243,7 +240,7 @@ async function deleteVideo(product: Product, video: { id?: number }) { await fetch(url, { method: 'DELETE', headers }); } -async function updateVideo(product: Product, video: { id?: number; url: string; description: string, title: string, }) { +async function updateVideo(product: Product, video: { id?: number; url: string; description: string; title: string }) { if (!video.id) { return; } diff --git a/frontend-app/src/types/Product.ts b/frontend-app/src/types/Product.ts index df35132..a90e6c1 100644 --- a/frontend-app/src/types/Product.ts +++ b/frontend-app/src/types/Product.ts @@ -12,7 +12,7 @@ export type Product = { physicalProperties: PhysicalProperties; circularityProperties: CircularityProperties; images: { id?: number; url: string; description: string }[]; - videos: { id?: number; url: string; description: string; title: string; }[]; + videos: { id?: number; url: string; description: string; title: string }[]; ownedBy: 'me' | string; amountInParent?: number; }; @@ -35,4 +35,3 @@ export type CircularityProperties = { repairabilityObservation: string; repairabilityReference?: string | null; }; - From ba4017b4f20f0e84bb3de602ad4f559e75f0afb2 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 27 Nov 2025 12:03:56 +0100 Subject: [PATCH 59/79] chore(backend): Simplify circularity properties model --- ...7_simplify_circularity_properties_model.py | 50 +++++++++++++++++++ backend/app/api/data_collection/models.py | 6 +-- 2 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 backend/alembic/versions/84d2f72dccc7_simplify_circularity_properties_model.py diff --git a/backend/alembic/versions/84d2f72dccc7_simplify_circularity_properties_model.py b/backend/alembic/versions/84d2f72dccc7_simplify_circularity_properties_model.py new file mode 100644 index 0000000..f62c861 --- /dev/null +++ b/backend/alembic/versions/84d2f72dccc7_simplify_circularity_properties_model.py @@ -0,0 +1,50 @@ +"""Simplify Circularity_properties model + +Revision ID: 84d2f72dccc7 +Revises: 0faa2fa19f62 +Create Date: 2025-11-27 12:01:32.413795 + +""" + +from collections.abc import Sequence +from typing import Union + +import sqlalchemy as sa +import sqlmodel + +import app.api.common.models.custom_types +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "84d2f72dccc7" +down_revision: str | None = "0faa2fa19f62" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "circularityproperties", "recyclability_observation", existing_type=sa.VARCHAR(length=500), nullable=True + ) + op.alter_column( + "circularityproperties", "repairability_observation", existing_type=sa.VARCHAR(length=500), nullable=True + ) + op.alter_column( + "circularityproperties", "remanufacturability_observation", existing_type=sa.VARCHAR(length=500), nullable=True + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "circularityproperties", "remanufacturability_observation", existing_type=sa.VARCHAR(length=500), nullable=False + ) + op.alter_column( + "circularityproperties", "repairability_observation", existing_type=sa.VARCHAR(length=500), nullable=False + ) + op.alter_column( + "circularityproperties", "recyclability_observation", existing_type=sa.VARCHAR(length=500), nullable=False + ) + # ### end Alembic commands ### diff --git a/backend/app/api/data_collection/models.py b/backend/app/api/data_collection/models.py index c981167..0e67d97 100644 --- a/backend/app/api/data_collection/models.py +++ b/backend/app/api/data_collection/models.py @@ -65,17 +65,17 @@ class CircularityPropertiesBase(CustomBase): """Base model to store circularity properties of a product.""" # Recyclability - recyclability_observation: str | None = Field(default=None, min_length=2, max_length=500) + recyclability_observation: str | None = Field(default=None, max_length=500) recyclability_comment: str | None = Field(default=None, max_length=100) recyclability_reference: str | None = Field(default=None, max_length=100) # Repairability - repairability_observation: str | None = Field(default=None, min_length=2, max_length=500) + repairability_observation: str | None = Field(default=None, max_length=500) repairability_comment: str | None = Field(default=None, max_length=100) repairability_reference: str | None = Field(default=None, max_length=100) # Remanufacturability - remanufacturability_observation: str | None = Field(default=None, min_length=2, max_length=500) + remanufacturability_observation: str | None = Field(default=None, max_length=500) remanufacturability_comment: str | None = Field(default=None, max_length=100) remanufacturability_reference: str | None = Field(default=None, max_length=100) From 0223b0ea3f6b701c071227bb43c68714b59cb7c2 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 27 Nov 2025 12:20:02 +0000 Subject: [PATCH 60/79] fix(docker): Update runtime stage python version to 3.14 in backend_migrations Docker image --- backend/Dockerfile.migrations | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Dockerfile.migrations b/backend/Dockerfile.migrations index 8d32ac9..47a017a 100644 --- a/backend/Dockerfile.migrations +++ b/backend/Dockerfile.migrations @@ -32,7 +32,7 @@ COPY scripts/ scripts/ COPY app/ app/ # --- Final runtime stage --- -FROM python:3.13-slim +FROM python:3.14-slim # Build arguments ARG WORKDIR=/opt/relab/backend_migrations From 671c0067d601335543c2513b2e74053799803684 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 27 Nov 2025 12:38:51 +0000 Subject: [PATCH 61/79] fix(frontend-app): omit circularityproperties if all fields are null --- frontend-app/src/services/api/saving.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend-app/src/services/api/saving.ts b/frontend-app/src/services/api/saving.ts index c422a4f..e9ec3d5 100644 --- a/frontend-app/src/services/api/saving.ts +++ b/frontend-app/src/services/api/saving.ts @@ -15,6 +15,7 @@ function toNewProduct(product: Product): any { brand: product.brand, model: product.model, description: product.description, + // TODO: Handle bill of materials properly bill_of_materials: [ { quantity: 42, @@ -55,7 +56,7 @@ function toUpdatePhysicalProperties(product: Product): any { } function toUpdateCircularityProperties(product: Product): any { - return { + const out = { recyclability_comment: product.circularityProperties.recyclabilityComment ?? null, recyclability_observation: product.circularityProperties.recyclabilityObservation, recyclability_reference: product.circularityProperties.recyclabilityReference ?? null, @@ -66,6 +67,10 @@ function toUpdateCircularityProperties(product: Product): any { repairability_observation: product.circularityProperties.repairabilityObservation, repairability_reference: product.circularityProperties.repairabilityReference ?? null, }; + + // If all values are null, return null so the caller can omit the object + const hasAny = Object.values(out).some((v) => v !== null); + return hasAny ? out : null; } export async function saveProduct(product: Product): Promise { From 6cf7e9f6cf4570fd4f06771e0a463346b3994eca Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 27 Nov 2025 13:59:00 +0000 Subject: [PATCH 62/79] fix(backend): Add some logging on rpi-cam related network errors --- .../rpi_cam/routers/camera_interaction/utils.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/backend/app/api/plugins/rpi_cam/routers/camera_interaction/utils.py b/backend/app/api/plugins/rpi_cam/routers/camera_interaction/utils.py index 178851e..485cbd4 100644 --- a/backend/app/api/plugins/rpi_cam/routers/camera_interaction/utils.py +++ b/backend/app/api/plugins/rpi_cam/routers/camera_interaction/utils.py @@ -4,7 +4,8 @@ from urllib.parse import urljoin from fastapi import HTTPException -from httpx import AsyncClient, Headers, HTTPStatusError, QueryParams, Response +from httpx import AsyncClient, Headers, HTTPStatusError, QueryParams, Response, RequestError +import logging from pydantic import UUID4 from sqlmodel.ext.asyncio.session import AsyncSession @@ -68,5 +69,13 @@ async def fetch_from_camera_url( status_code=e.response.status_code, detail={"main API": error_msg, "Camera API": e.response.json().get("detail")}, ) from e + except RequestError as e: + # Network-level errors (DNS, connection refused, timeouts). + logger = logging.getLogger(__name__) + logger.warning("Network error contacting camera %s%s: %s", camera.url, endpoint, e) + raise HTTPException(status_code=503, detail={ + "main API": f"Network error contacting camera: {endpoint}", + "error": str(e), + }) from e else: return response From f68a9500abc25a86f7753c5bc46c06efd4ff0467 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 27 Nov 2025 15:09:14 +0100 Subject: [PATCH 63/79] fix(backend): Remove unused docker arg --- backend/Dockerfile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 17b19a7..fb580fc 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -38,7 +38,6 @@ FROM python:3.14-slim # Build arguments ARG WORKDIR=/opt/relab/backend -ARG APP_PORT=8000 ARG APP_USER=appuser # Set up a non-root user @@ -54,11 +53,11 @@ ENV PYTHONPATH=$WORKDIR \ PYTHONUNBUFFERED=1 \ PATH="$WORKDIR/.venv/bin:$PATH" -# Expose the application port -EXPOSE 8000 - # Switch to non-root user USER $APP_USER +# Expose the application port +EXPOSE 8000 + # Run the FastAPI application CMD [".venv/bin/fastapi", "run", "app/main.py", "--host", "0.0.0.0", "--port", "8000"] From ef7deb59a4bf310ff2a3eb2d8a8cf955d4d96d55 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 1 Dec 2025 14:04:55 +0000 Subject: [PATCH 64/79] fix(backend): update ruff target python version --- backend/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 042eaca..641d3f9 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -118,7 +118,7 @@ [tool.ruff] fix = true line-length = 120 - target-version = "py313" + target-version = "py314" # Exclude automatically generated files from linting extend-exclude = ["./alembic/versions"] From 1ef6d7a2b72a668ee3a5fa6fc667364b57ac75e2 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 1 Dec 2025 17:11:45 +0100 Subject: [PATCH 65/79] fix(backend): Update python target version for ruff to 3.14 --- backend/app/api/auth/schemas.py | 8 ++++---- backend/app/api/auth/utils/context_managers.py | 2 +- backend/app/api/background_data/schemas.py | 6 +++--- backend/app/api/common/models/associations.py | 4 ++-- backend/app/api/data_collection/schemas.py | 6 +++--- .../rpi_cam/routers/camera_interaction/utils.py | 15 +++++++++------ backend/pyproject.toml | 1 - 7 files changed, 22 insertions(+), 20 deletions(-) diff --git a/backend/app/api/auth/schemas.py b/backend/app/api/auth/schemas.py index 6abd33f..8db9611 100644 --- a/backend/app/api/auth/schemas.py +++ b/backend/app/api/auth/schemas.py @@ -31,13 +31,13 @@ class OrganizationRead(OrganizationBase): class OrganizationReadWithRelationshipsPublic(BaseReadSchemaWithTimeStamp, OrganizationBase): """Read schema for organizations, including relationships.""" - members: list["UserReadPublic"] = Field(default_factory=list, description="List of users in the organization.") + members: list[UserReadPublic] = Field(default_factory=list, description="List of users in the organization.") class OrganizationReadWithRelationships(BaseReadSchemaWithTimeStamp, OrganizationBase): """Read schema for organizations, including relationships.""" - members: list["UserRead"] = Field(default_factory=list, description="List of users in the organization.") + members: list[UserRead] = Field(default_factory=list, description="List of users in the organization.") class OrganizationUpdate(BaseUpdateSchema): @@ -92,7 +92,7 @@ class UserCreate(UserCreateBase): class UserCreateWithOrganization(UserCreateBase): """Create schema for users with organization to create and own.""" - organization: "OrganizationCreate" + organization: OrganizationCreate model_config: ConfigDict = ConfigDict( # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 { @@ -140,7 +140,7 @@ class UserRead(UserBase, schemas.BaseUser[uuid.UUID]): class UserReadWithOrganization(UserRead): """Read schema for users with organization.""" - organization: Optional["OrganizationRead"] = Field(default=None, description="Organization the user belongs to.") + organization: OrganizationRead | None = Field(default=None, description="Organization the user belongs to.") class UserReadWithRelationships(UserReadWithOrganization): diff --git a/backend/app/api/auth/utils/context_managers.py b/backend/app/api/auth/utils/context_managers.py index 9823ba6..af40334 100644 --- a/backend/app/api/auth/utils/context_managers.py +++ b/backend/app/api/auth/utils/context_managers.py @@ -19,7 +19,7 @@ @asynccontextmanager async def get_chained_async_user_manager_context( session: AsyncSession | None = None, -) -> AsyncGenerator["UserManager"]: +) -> AsyncGenerator[UserManager]: """Provides a user manager context using the user database and an async database session. If a session is provided, it will be used; otherwise, a new session for the default database will be created. diff --git a/backend/app/api/background_data/schemas.py b/backend/app/api/background_data/schemas.py index b82b280..682a6c2 100644 --- a/backend/app/api/background_data/schemas.py +++ b/backend/app/api/background_data/schemas.py @@ -33,7 +33,7 @@ class CategoryCreateWithinCategoryWithSubCategories(BaseCreateSchema, CategoryBa """Schema for creating a new category within a category, with optional subcategories.""" # Database model has a None default, but Pydantic model has empty set default for consistent API type handling - subcategories: set["CategoryCreateWithinCategoryWithSubCategories"] = Field( + subcategories: set[CategoryCreateWithinCategoryWithSubCategories] = Field( default_factory=set, description="List of subcategories", ) @@ -97,7 +97,7 @@ class CategoryReadWithRelationships(CategoryRead): """Schema for reading category information with all relationships.""" materials: list[MaterialRead] = Field(default_factory=list, description="List of materials linked to the category") - product_types: list["ProductTypeRead"] = Field( + product_types: list[ProductTypeRead] = Field( default_factory=list, description="List of product types linked to the category" ) @@ -111,7 +111,7 @@ class CategoryReadWithRelationshipsAndFlatSubCategories(CategoryReadWithRelation class CategoryReadAsSubCategoryWithRecursiveSubCategories(CategoryReadAsSubCategory): """Schema for reading category information with recursive subcategories.""" - subcategories: list["CategoryReadAsSubCategoryWithRecursiveSubCategories"] = Field( + subcategories: list[CategoryReadAsSubCategoryWithRecursiveSubCategories] = Field( default_factory=list, description="List of subcategories" ) diff --git a/backend/app/api/common/models/associations.py b/backend/app/api/common/models/associations.py index e0da3a4..e19285c 100644 --- a/backend/app/api/common/models/associations.py +++ b/backend/app/api/common/models/associations.py @@ -34,8 +34,8 @@ class MaterialProductLink(MaterialProductLinkBase, TimeStampMixinBare, table=Tru foreign_key="product.id", primary_key=True, description="ID of the product with the material" ) - material: "Material" = Relationship(back_populates="product_links", sa_relationship_kwargs={"lazy": "selectin"}) - product: "Product" = Relationship(back_populates="bill_of_materials", sa_relationship_kwargs={"lazy": "selectin"}) + material: Material = Relationship(back_populates="product_links", sa_relationship_kwargs={"lazy": "selectin"}) + product: Product = Relationship(back_populates="bill_of_materials", sa_relationship_kwargs={"lazy": "selectin"}) def __str__(self) -> str: return f"{self.quantity} {self.unit} of {self.material.name} in {self.product.name}" diff --git a/backend/app/api/data_collection/schemas.py b/backend/app/api/data_collection/schemas.py index 32794af..0471fa9 100644 --- a/backend/app/api/data_collection/schemas.py +++ b/backend/app/api/data_collection/schemas.py @@ -256,7 +256,7 @@ class ComponentCreateWithComponents(ComponentCreate): """ # Recursive components - components: list["ComponentCreateWithComponents"] = Field( + components: list[ComponentCreateWithComponents] = Field( default_factory=list, description="Set of component products" ) @@ -309,13 +309,13 @@ class ProductReadWithRelationships(ProductReadWithProperties): class ProductReadWithRelationshipsAndFlatComponents(ProductReadWithRelationships): """Schema for reading product information with one level of components.""" - components: list["ComponentRead"] = Field(default_factory=list, description="List of component products") + components: list[ComponentRead] = Field(default_factory=list, description="List of component products") class ComponentReadWithRecursiveComponents(ComponentRead): """Schema for reading product information with recursive components.""" - components: list["ComponentReadWithRecursiveComponents"] = Field( + components: list[ComponentReadWithRecursiveComponents] = Field( default_factory=list, description="List of component products" ) diff --git a/backend/app/api/plugins/rpi_cam/routers/camera_interaction/utils.py b/backend/app/api/plugins/rpi_cam/routers/camera_interaction/utils.py index 485cbd4..23f8f5a 100644 --- a/backend/app/api/plugins/rpi_cam/routers/camera_interaction/utils.py +++ b/backend/app/api/plugins/rpi_cam/routers/camera_interaction/utils.py @@ -1,11 +1,11 @@ """Utilities for the camera interaction endpoints.""" +import logging from enum import Enum from urllib.parse import urljoin from fastapi import HTTPException -from httpx import AsyncClient, Headers, HTTPStatusError, QueryParams, Response, RequestError -import logging +from httpx import AsyncClient, Headers, HTTPStatusError, QueryParams, RequestError, Response from pydantic import UUID4 from sqlmodel.ext.asyncio.session import AsyncSession @@ -73,9 +73,12 @@ async def fetch_from_camera_url( # Network-level errors (DNS, connection refused, timeouts). logger = logging.getLogger(__name__) logger.warning("Network error contacting camera %s%s: %s", camera.url, endpoint, e) - raise HTTPException(status_code=503, detail={ - "main API": f"Network error contacting camera: {endpoint}", - "error": str(e), - }) from e + raise HTTPException( + status_code=503, + detail={ + "main API": f"Network error contacting camera: {endpoint}", + "error": str(e), + }, + ) from e else: return response diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 641d3f9..9c31527 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -137,7 +137,6 @@ "C4", # flake8-comprehensions (fixes iterable comprehensions) "C90", # mccabe "D", # pydocstyle - "DJ", # flake8-django "DTZ", # flake8-datetimez (checks for naive datetime uses without timezone) "E", # pycodestyle errors "EM", # flake8-errmsgs (checks for error messages) From cbdfbc376ba1e9a2a0d508f0a5f94c57f8f994fc Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 1 Dec 2025 17:12:35 +0100 Subject: [PATCH 66/79] fix(backend): Move logging module --- backend/app/core/{utils/custom_logging.py => logging.py} | 0 backend/app/core/utils/__init__.py | 1 - backend/app/main.py | 2 +- 3 files changed, 1 insertion(+), 2 deletions(-) rename backend/app/core/{utils/custom_logging.py => logging.py} (100%) delete mode 100644 backend/app/core/utils/__init__.py diff --git a/backend/app/core/utils/custom_logging.py b/backend/app/core/logging.py similarity index 100% rename from backend/app/core/utils/custom_logging.py rename to backend/app/core/logging.py diff --git a/backend/app/core/utils/__init__.py b/backend/app/core/utils/__init__.py deleted file mode 100644 index 9b957b1..0000000 --- a/backend/app/core/utils/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Cross-package utility functions.""" diff --git a/backend/app/main.py b/backend/app/main.py index 4b8807b..88c4e9e 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -20,8 +20,8 @@ from app.api.common.routers.openapi import init_openapi_docs from app.core.config import settings from app.core.database import async_engine +from app.core.logging import setup_logging from app.core.redis import close_redis, init_redis -from app.core.utils.custom_logging import setup_logging # Initialize logging setup_logging() From 324ce8e2c5d24eff4fb08feba071f7e79d4295f2 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 2 Dec 2025 11:01:12 +0100 Subject: [PATCH 67/79] fix(backend): Lint backend with ruff targeted to 3.14 and remove unused sqladmin interface --- CONTRIBUTING.md | 5 +- backend/.env.example | 2 +- backend/alembic/env.py | 3 +- backend/app/api/admin/__init__.py | 1 - backend/app/api/admin/auth.py | 76 ----- backend/app/api/admin/config.py | 13 - backend/app/api/admin/main.py | 65 ---- backend/app/api/admin/models.py | 306 ------------------ backend/app/api/auth/models.py | 10 +- backend/app/api/auth/routers/frontend.py | 2 - backend/app/api/background_data/models.py | 14 +- backend/app/api/data_collection/models.py | 22 +- backend/app/api/file_storage/models/models.py | 12 +- backend/app/api/plugins/rpi_cam/models.py | 2 +- backend/app/main.py | 12 +- backend/app/templates/index.html | 7 - backend/pyproject.toml | 3 +- backend/uv.lock | 30 -- docs/docs/architecture/system-design.md | 4 - frontend-app/src/app/products/[id]/camera.tsx | 255 +++++++++++---- frontend-app/src/services/api/saving.ts | 6 +- 21 files changed, 230 insertions(+), 620 deletions(-) delete mode 100644 backend/app/api/admin/__init__.py delete mode 100644 backend/app/api/admin/auth.py delete mode 100644 backend/app/api/admin/config.py delete mode 100644 backend/app/api/admin/main.py delete mode 100644 backend/app/api/admin/models.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b151b2d..8e89656 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -173,10 +173,7 @@ It is still recommended to use VS Code as your IDE, as we have provided some rec The API is now available at . - You can log in with the superuser details specified in the `.env` file. This gives you access to: - - - Interactive API documentation at - - Admin panel for database management at + You can log in with the superuser details specified in the `.env` file. This gives you access to the interactive API documentation at #### Documentation Setup diff --git a/backend/.env.example b/backend/.env.example index 1cf0df5..bec577f 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -32,7 +32,7 @@ EMAIL_REPLY_TO='your.replyto.alias.@example.com' # 🔀 Email address REDIS_HOST='localhost' # Redis server host (use 'cache' in Docker) REDIS_PORT='6379' # Redis server port REDIS_DB='0' # Redis database number (0-15) -REDIS_PASSWORD='' # 🔀 Redis password (leave empty if no password) +REDIS_PASSWORD='' # 🔀 Redis password (leave empty if no password) # Superuser details SUPERUSER_EMAIL='your-email@example.com' # 🔀 diff --git a/backend/alembic/env.py b/backend/alembic/env.py index 397a112..ef4b0b2 100644 --- a/backend/alembic/env.py +++ b/backend/alembic/env.py @@ -4,10 +4,11 @@ from pathlib import Path import alembic_postgresql_enum # noqa: F401 (Make sure the PostgreSQL ENUM type is recognized) -from alembic import context from sqlalchemy import engine_from_config, pool from sqlmodel import SQLModel # Include the SQLModel metadata +from alembic import context + # Load settings from the FastAPI app config project_root = Path(__file__).resolve().parents[1] sys.path.append(str(project_root)) diff --git a/backend/app/api/admin/__init__.py b/backend/app/api/admin/__init__.py deleted file mode 100644 index 180309f..0000000 --- a/backend/app/api/admin/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Admin panel package.""" diff --git a/backend/app/api/admin/auth.py b/backend/app/api/admin/auth.py deleted file mode 100644 index f3916af..0000000 --- a/backend/app/api/admin/auth.py +++ /dev/null @@ -1,76 +0,0 @@ -"""Authentication backend for the SQLAdmin interface, based on FastAPI-Users authentication backend.""" - -import json -from typing import Literal - -from fastapi import Response, status -from fastapi.responses import RedirectResponse -from sqladmin.authentication import AuthenticationBackend -from sqlalchemy.ext.asyncio import async_sessionmaker -from sqlmodel.ext.asyncio.session import AsyncSession -from starlette.requests import Request - -from app.api.admin.config import settings as admin_settings -from app.api.auth.config import settings as auth_settings -from app.api.auth.routers.frontend import router as frontend_auth_router -from app.api.auth.services.user_manager import cookie_transport, get_jwt_strategy -from app.api.auth.utils.context_managers import get_chained_async_user_manager_context -from app.core.database import async_engine - -async_session_generator = async_sessionmaker(bind=async_engine, class_=AsyncSession, expire_on_commit=False) - -# TODO: Redirect all backend login systems (admin panel, swagger docs, API landing page) to frontend login system -main_login_page_redirect_path = ( - f"{frontend_auth_router.url_path_for('login_page')}?next={admin_settings.admin_base_url}" -) - - -class AdminAuth(AuthenticationBackend): - """Authentication backend for the SQLAdmin interface, using FastAPI-Users.""" - - async def login(self, request: Request) -> bool: # noqa: ARG002 # Signature expected by the SQLAdmin implementation - """Placeholder logout function. - - Login is handled by the authenticate method, which redirects to the main API login page. - """ - return True - - async def logout(self, request: Request) -> bool: # noqa: ARG002 # Signature expected by the SQLAdmin implementation - """Placeholder logout function. - - Logout requires unsetting a cookie, which is not possible in the standard SQLAdmin logout function, - which is excepted to return a boolean. - Instead, the default logout route is overridden by the custom route below. - """ - return True - - async def authenticate(self, request: Request) -> RedirectResponse | Response | Literal[True]: - token = request.cookies.get(cookie_transport.cookie_name) - if not token: - return RedirectResponse(url=main_login_page_redirect_path) - async with get_chained_async_user_manager_context() as user_manager: - user = await get_jwt_strategy().read_token(token=token, user_manager=user_manager) - if user is None: - return RedirectResponse(url=main_login_page_redirect_path) - if not user.is_superuser: - return Response( - json.dumps({"detail": "You do not have permission to access this resource."}), - status_code=status.HTTP_403_FORBIDDEN, - media_type="application/json", - ) - - return True - - -def get_authentication_backend() -> AdminAuth: - """Get the authentication backend for the SQLAdmin interface.""" - return AdminAuth(secret_key=auth_settings.fastapi_users_secret.get_secret_value()) - - -async def logout_override(request: Request) -> RedirectResponse: # noqa: ARG001 # Signature expected by the SQLAdmin implementation - """Override of the default admin dashboard logout route to unset the authentication cookie.""" - response = RedirectResponse(url=frontend_auth_router.url_path_for("index"), status_code=302) - response.delete_cookie( - key=cookie_transport.cookie_name, domain=cookie_transport.cookie_domain, path=cookie_transport.cookie_path - ) - return response diff --git a/backend/app/api/admin/config.py b/backend/app/api/admin/config.py deleted file mode 100644 index 7ed8416..0000000 --- a/backend/app/api/admin/config.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Configuration for the admin module.""" - -from pydantic_settings import BaseSettings - - -class AdminSettings(BaseSettings): - """Settings class to store settings related to admin components.""" - - admin_base_url: str = "/admin/dashboard" # The base url of the SQLadmin interface - - -# Create a settings instance that can be imported throughout the app -settings = AdminSettings() diff --git a/backend/app/api/admin/main.py b/backend/app/api/admin/main.py deleted file mode 100644 index df5aa67..0000000 --- a/backend/app/api/admin/main.py +++ /dev/null @@ -1,65 +0,0 @@ -"""SQLAdmin module for the FastAPI app.""" - -from fastapi import FastAPI -from sqladmin import Admin -from sqlalchemy import Engine -from sqlalchemy.ext.asyncio.engine import AsyncEngine -from starlette.applications import Starlette -from starlette.routing import Mount, Route - -from app.api.admin.auth import get_authentication_backend, logout_override -from app.api.admin.config import settings -from app.api.admin.models import ( - CategoryAdmin, - ImageAdmin, - MaterialAdmin, - MaterialProductLinkAdmin, - ProductAdmin, - ProductTypeAdmin, - TaxonomyAdmin, - UserAdmin, - VideoAdmin, -) - - -def init_admin(app: FastAPI, engine: Engine | AsyncEngine) -> Admin: - """Initialize the SQLAdmin interface for the FastAPI app. - - Args: - app (FastAPI): Main FastAPI application instance - engine (Engine | AsyncEngine): SQLAlchemy database engine, sync or async - """ - admin = Admin(app, engine, authentication_backend=get_authentication_backend(), base_url=settings.admin_base_url) - - # HACK: Override SQLAdmin logout route to allow cookie-based auth - for route in admin.app.routes: - # Find the mounted SQLAdmin app - if isinstance(route, Mount) and route.path == settings.admin_base_url and isinstance(route.app, Starlette): - for subroute in route.app.routes: - # Find the logout subroute and replace it with the custom override to allow cookie-based auth - if isinstance(subroute, Route) and subroute.name == "logout": - route.routes.remove(subroute) - route.app.add_route( - subroute.path, - logout_override, - methods=list(subroute.methods) if subroute.methods is not None else None, - name="logout", - ) - break - break - - # Add Background Data views to Admin interface - admin.add_view(CategoryAdmin) - admin.add_view(MaterialAdmin) - admin.add_view(ProductTypeAdmin) - admin.add_view(TaxonomyAdmin) - # Add Data Collection views to Admin interface - admin.add_view(MaterialProductLinkAdmin) - admin.add_view(ImageAdmin) - admin.add_view(ProductAdmin) - admin.add_view(VideoAdmin) - - # Add other admin views - admin.add_view(UserAdmin) - - return admin diff --git a/backend/app/api/admin/models.py b/backend/app/api/admin/models.py deleted file mode 100644 index f0efff6..0000000 --- a/backend/app/api/admin/models.py +++ /dev/null @@ -1,306 +0,0 @@ -"""Models for the admin module.""" - -import uuid -from collections.abc import Callable, Sequence -from pathlib import Path -from typing import Any, ClassVar - -from anyio import to_thread -from markupsafe import Markup -from sqladmin import ModelView -from sqladmin._types import MODEL_ATTR -from starlette.datastructures import UploadFile -from starlette.requests import Request -from wtforms import ValidationError -from wtforms.fields import FileField -from wtforms.form import Form -from wtforms.validators import InputRequired - -from app.api.auth.models import User -from app.api.background_data.models import Category, Material, ProductType, Taxonomy -from app.api.common.models.associations import MaterialProductLink -from app.api.data_collection.models import Product -from app.api.file_storage.models.models import Image, Video - -### Constants ### -ALLOWED_IMAGE_EXTENSIONS: set[str] = {".bmp", ".gif", ".jpeg", ".jpg", ".png", ".tiff", ".webp"} - - -### Form Validators ### -class FileSizeLimit: - """WTForms validator to limit the file size of a FileField.""" - - def __init__(self, max_size_mb: int, message: str | None = None) -> None: - self.max_size_mb = max_size_mb - self.message = message or f"File size must be under {self.max_size_mb} MB." - - def __call__(self, form: Form, field: FileField): # noqa: ARG002 # WTForms uses this signature - if isinstance(field.data, UploadFile) and field.data.size and field.data.size > self.max_size_mb * 1024 * 1024: - raise ValidationError(self.message) - - -class FileTypeValidator: - """WTForms validator to limit the file type of a FileField.""" - - def __init__(self, allowed_extensions: set[str], message: str | None = None): - self.allowed_extensions = allowed_extensions - self.message = message or f"Allowed file types: {', '.join(self.allowed_extensions)}." - - def __call__(self, form: Form, field: FileField): # noqa: ARG002 # WTForms uses this signature - if isinstance(field.data, UploadFile) and field.data.filename: - file_ext = Path(field.data.filename).suffix.lower() - if file_ext not in self.allowed_extensions: - raise ValidationError(self.message) - - -### Linking Models ### -class MaterialProductLinkAdmin(ModelView, model=MaterialProductLink): - """Admin view for Material-Product links.""" - - name = "Material-Product Link" - name_plural = "Material-Product Links" - icon = "fa-solid fa-link" - category = "Data Collection" - - column_list: ClassVar[Sequence[MODEL_ATTR]] = ["material", "product", "quantity", "unit"] - - column_formatters: ClassVar[dict[MODEL_ATTR, Callable]] = { - "material": lambda m, _: Markup('{}').format(m.material_id, m.material), - "product": lambda m, _: Markup('{}').format(m.product_id, m.product), - } - - column_searchable_list: ClassVar[Sequence[MODEL_ATTR]] = ["material.name", "product.name"] - - column_sortable_list: ClassVar[Sequence[MODEL_ATTR]] = ["quantity", "unit"] - - column_details_list: ClassVar[Sequence[MODEL_ATTR]] = [*column_list, "created_at", "updated_at"] - - -### Background Models ### -class CategoryAdmin(ModelView, model=Category): - """Admin view for Category model.""" - - name = "Category" - name_plural = "Categories" - icon = "fa-solid fa-list" - category = "Background Data" - column_list: ClassVar[Sequence[MODEL_ATTR]] = ["id", "name", "taxonomy_id"] - column_searchable_list: ClassVar[Sequence[MODEL_ATTR]] = ["name", "description"] - column_sortable_list: ClassVar[Sequence[MODEL_ATTR]] = ["id", "name", "taxonomy_id"] - - -class TaxonomyAdmin(ModelView, model=Taxonomy): - """Admin view for Taxonomy model.""" - - name = "Taxonomy" - name_plural = "Taxonomies" - icon = "fa-solid fa-sitemap" - category = "Background Data" - - column_list: ClassVar[Sequence[MODEL_ATTR]] = ["id", "name", "domain"] - column_searchable_list: ClassVar[Sequence[MODEL_ATTR]] = ["name", "domain"] - column_sortable_list: ClassVar[Sequence[MODEL_ATTR]] = ["id", "name"] - - -class MaterialAdmin(ModelView, model=Material): - """Admin view for Material model.""" - - name = "Material" - name_plural = "Materials" - icon = "fa-solid fa-cubes" - category = "Background Data" - - column_labels: ClassVar[dict[MODEL_ATTR, str]] = { - "density_kg_m3": "Density (kg/m³)", - "is_crm": "Is CRM", - } - - column_list: ClassVar[Sequence[MODEL_ATTR]] = [ - "id", - "name", - "description", - "is_crm", - ] - column_searchable_list: ClassVar[Sequence[MODEL_ATTR]] = ["name", "description"] - column_sortable_list: ClassVar[Sequence[MODEL_ATTR]] = ["id", "name", "is_crm"] - - -class ProductTypeAdmin(ModelView, model=ProductType): - """Admin view for ProductType model.""" - - name = "Product Type" - name_plural = "Product Types" - icon = "fa-solid fa-tag" - category = "Background Data" - - column_labels: ClassVar[dict[MODEL_ATTR, str]] = { - "lifespan_yr": "Lifespan (years)", - } - - column_list: ClassVar[Sequence[MODEL_ATTR]] = ["id", "name", "description"] - column_searchable_list: ClassVar[Sequence[MODEL_ATTR]] = ["name", "description"] - column_sortable_list: ClassVar[Sequence[MODEL_ATTR]] = ["id", "name"] - - -### Product Models ### -class ProductAdmin(ModelView, model=Product): - """Admin view for Product model.""" - - name = "Product" - name_plural = "Products" - icon = "fa-solid fa-box" - category = "Data Collection" - - column_list: ClassVar[Sequence[MODEL_ATTR]] = [ - "id", - "name", - "type", - "description", - ] - column_searchable_list: ClassVar[Sequence[MODEL_ATTR]] = ["name", "description"] - column_sortable_list: ClassVar[Sequence[MODEL_ATTR]] = [ - "id", - "name", - "product_type_id", - ] - - -### Data Collection Models ### -class VideoAdmin(ModelView, model=Video): - """Admin view for Video model.""" - - name = "Video" - name_plural = "Videos" - icon = "fa-solid fa-video" - category = "Data Collection" - - column_list: ClassVar[Sequence[MODEL_ATTR]] = ["id", "url", "description", "product", "created_at"] - - column_formatters: ClassVar[dict[MODEL_ATTR, Callable]] = { - "url": lambda m, _: Markup('{}').format(m.url, m.url), - "product": lambda m, _: Markup('{}').format(m.product_id, m.product) - if m.product - else "", - "created_at": lambda m, _: m.created_at.strftime("%Y-%m-%d %H:%M") if m.created_at else "", - } - - column_searchable_list: ClassVar[Sequence[MODEL_ATTR]] = ["description", "url"] - - column_sortable_list: ClassVar[Sequence[MODEL_ATTR]] = ["id", "created_at"] - - column_details_list: ClassVar[Sequence[MODEL_ATTR]] = [*column_list, "updated_at"] - - -### User Models ### -class UserAdmin(ModelView, model=User): - """Admin view for User model.""" - - name = "User" - name_plural = "Users" - icon = "fa-solid fa-user" - category = "Users" - - # User CRUD should be handled by the auth module - can_create = False - can_edit = False - can_delete = False - - column_list: ClassVar[Sequence[MODEL_ATTR]] = [ - "id", - "email", - "username", - "organization", - "is_active", - "is_superuser", - "is_verified", - ] - column_searchable_list: ClassVar[Sequence[MODEL_ATTR]] = ["email", "organization"] - column_sortable_list: ClassVar[Sequence[MODEL_ATTR]] = ["email", "organization"] - - column_details_list: ClassVar[Sequence[MODEL_ATTR]] = column_list - - -### File Storage Models ### -class ImageAdmin(ModelView, model=Image): - """Admin view for Image model.""" - - # TODO: Use Image schema logic instead of duplicating it here - # TODO: Add a method to download the original file (should take it from the filename but rename it to original_name) - - name = "Image" - name_plural = "Images" - icon = "fa-solid fa-camera" - category = "Data Collection" - - # Display settings - column_list: ClassVar[Sequence[MODEL_ATTR]] = [ - "id", - "description", - "filename", - "created_at", - "updated_at", - "image_preview", - ] - column_details_list: ClassVar[Sequence[MODEL_ATTR]] = column_list - column_formatters: ClassVar[dict[MODEL_ATTR, Callable]] = { - "created_at": lambda model, _: model.created_at.strftime("%Y-%m-%d %H:%M:%S") if model.created_at else "", - "updated_at": lambda model, _: model.updated_at.strftime("%Y-%m-%d %H:%M:%S") if model.updated_at else "", - "image_preview": lambda model, _: model.image_preview(100), - } - column_formatters_detail: ClassVar[dict[MODEL_ATTR, Callable]] = column_formatters - - column_searchable_list: ClassVar[Sequence[MODEL_ATTR]] = [ - "id", - "description", - "filename", - "created_at", - "updated_at", - ] - column_sortable_list: ClassVar[Sequence[MODEL_ATTR]] = column_searchable_list - - # Create and edit settings - form_columns: ClassVar[Sequence[MODEL_ATTR]] = [ - "description", - "file", - ] - - form_args: ClassVar[dict[str, Any]] = { - "file": { - "validators": [ - InputRequired(), - FileSizeLimit(max_size_mb=10), - FileTypeValidator(allowed_extensions=ALLOWED_IMAGE_EXTENSIONS), - ], - } - } - - def _delete_image_file(self, image_path: Path) -> None: - """Delete the image file from the filesystem if it exists.""" - if image_path.exists(): - image_path.unlink() - - def handle_model_change(self, data: dict[str, Any], model: Image, is_created: bool) -> None: # noqa: FBT001 # Wtforms uses this signature - def new_image_uploaded(data: dict[str, Any]) -> bool: - """Check if a new image is present in form data.""" - return isinstance(data.get("file"), UploadFile) and data["file"].size - - if new_image_uploaded(data): - model.filename = data["file"].filename # Set the filename to the original filename - data["file"].filename = f"{uuid.uuid4()}{Path(model.filename).suffix}" # Store the file to a unique path - - if not is_created and model.file: # If the model is being edited and it has an existing image - if new_image_uploaded(data): - self._delete_image_file(Path(model.file.path)) - else: - data.pop("file", None) # Keep existing image if no new one uploaded - - def handle_model_delete(self, model: Image) -> None: - if model.file: - self._delete_image_file(model.file.path) - - async def on_model_change(self, data: dict[str, Any], model: Image, is_created: bool, request: Request) -> None: # noqa: ARG002, FBT001 # Wtforms uses this signature - """SQLAdmin expects on_model_change to be asynchronous. This method handles the synchronous model change.""" - await to_thread.run_sync(self.handle_model_change, data, model, is_created) - - async def after_model_delete(self, model: Image, request: Request) -> None: # noqa: ARG002 # Wtforms uses this signature - await to_thread.run_sync(lambda: self._delete_image_file(Path(model.file.path)) if model.file.path else None) diff --git a/backend/app/api/auth/models.py b/backend/app/api/auth/models.py index 206f81a..afe73c1 100644 --- a/backend/app/api/auth/models.py +++ b/backend/app/api/auth/models.py @@ -43,7 +43,7 @@ class User(SQLModelBaseUserDB, CustomBaseBare, UserBase, TimeStampMixinBare, tab id: UUID4 | None = Field(default_factory=uuid.uuid4, primary_key=True, nullable=False) # One-to-many relationship with OAuthAccount - oauth_accounts: list["OAuthAccount"] = Relationship( + oauth_accounts: list[OAuthAccount] = Relationship( back_populates="user", sa_relationship_kwargs={ "lazy": "joined", # Required because of FastAPI-Users OAuth implementation @@ -52,7 +52,7 @@ class User(SQLModelBaseUserDB, CustomBaseBare, UserBase, TimeStampMixinBare, tab }, # TODO: Check if this is fixed in future versions of pydantic/sqlmodel and we can use automatic # relationship detection again ) - products: list["Product"] = Relationship( + products: list[Product] = Relationship( back_populates="owner", sa_relationship_kwargs={ "primaryjoin": "User.id == Product.owner_id", # HACK: Explicitly define join condition because of @@ -68,7 +68,7 @@ class User(SQLModelBaseUserDB, CustomBaseBare, UserBase, TimeStampMixinBare, tab nullable=True, ), ) - organization: Optional["Organization"] = Relationship( + organization: Organization | None = Relationship( back_populates="members", sa_relationship_kwargs={ "lazy": "selectin", @@ -79,7 +79,7 @@ class User(SQLModelBaseUserDB, CustomBaseBare, UserBase, TimeStampMixinBare, tab organization_role: OrganizationRole | None = Field(default=None, sa_column=Column(SAEnum(OrganizationRole))) # One-to-one relationship with owned Organization - owned_organization: Optional["Organization"] = Relationship( + owned_organization: Organization | None = Relationship( back_populates="owner", sa_relationship_kwargs={ "uselist": False, @@ -146,7 +146,7 @@ class Organization(OrganizationBase, TimeStampMixinBare, table=True): ) # One-to-many relationship with member Users - members: list["User"] = Relationship( + members: list[User] = Relationship( back_populates="organization", sa_relationship_kwargs={ "primaryjoin": "Organization.id == User.organization_id", diff --git a/backend/app/api/auth/routers/frontend.py b/backend/app/api/auth/routers/frontend.py index 8a83909..6b2aafd 100644 --- a/backend/app/api/auth/routers/frontend.py +++ b/backend/app/api/auth/routers/frontend.py @@ -6,7 +6,6 @@ from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates -from app.api.admin.config import settings as admin_settings from app.api.auth.dependencies import OptionalCurrentActiveUserDep from app.core.config import settings as core_settings @@ -30,7 +29,6 @@ async def index( "user": user, "show_full_docs": user.is_superuser if user else False, "frontend_web_url": core_settings.frontend_web_url, - "admin_path": admin_settings.admin_base_url, }, ) diff --git a/backend/app/api/background_data/models.py b/backend/app/api/background_data/models.py index d03a295..1600452 100644 --- a/backend/app/api/background_data/models.py +++ b/backend/app/api/background_data/models.py @@ -64,7 +64,7 @@ class Taxonomy(TaxonomyBase, TimeStampMixinBare, table=True): id: int | None = Field(default=None, primary_key=True) - categories: list["Category"] = Relationship(back_populates="taxonomy", cascade_delete=True) + categories: list[Category] = Relationship(back_populates="taxonomy", cascade_delete=True) model_config: ConfigDict = ConfigDict(use_enum_values=True, arbitrary_types_allowed=True) # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 @@ -89,11 +89,11 @@ class Category(CategoryBase, TimeStampMixinBare, table=True): # Self-referential relationship supercategory_id: int | None = Field(foreign_key="category.id", default=None, nullable=True) - supercategory: Optional["Category"] = Relationship( + supercategory: Category | None = Relationship( back_populates="subcategories", sa_relationship_kwargs={"remote_side": "Category.id", "lazy": "selectin", "join_depth": 1}, ) - subcategories: list["Category"] | None = Relationship( + subcategories: list[Category] | None = Relationship( back_populates="supercategory", sa_relationship_kwargs={"lazy": "selectin", "join_depth": 1}, cascade_delete=True, @@ -104,8 +104,8 @@ class Category(CategoryBase, TimeStampMixinBare, table=True): taxonomy: Taxonomy = Relationship(back_populates="categories") # Many-to-many relationships. This is ugly but SQLModel doesn't allow for polymorphic association. - materials: list["Material"] | None = Relationship(back_populates="categories", link_model=CategoryMaterialLink) - product_types: list["ProductType"] | None = Relationship( + materials: list[Material] | None = Relationship(back_populates="categories", link_model=CategoryMaterialLink) + product_types: list[ProductType] | None = Relationship( back_populates="categories", link_model=CategoryProductTypeLink ) @@ -138,7 +138,7 @@ class Material(MaterialBase, TimeStampMixinBare, table=True): # Many-to-many relationships categories: list[Category] | None = Relationship(back_populates="materials", link_model=CategoryMaterialLink) - product_links: list["MaterialProductLink"] | None = Relationship(back_populates="material") + product_links: list[MaterialProductLink] | None = Relationship(back_populates="material") # Magic methods def __str__(self) -> str: @@ -159,7 +159,7 @@ class ProductType(ProductTypeBase, TimeStampMixinBare, table=True): id: int | None = Field(default=None, primary_key=True) # One-to-many relationships - products: list["Product"] | None = Relationship(back_populates="product_type") + products: list[Product] | None = Relationship(back_populates="product_type") files: list[File] | None = Relationship(back_populates="product_type", cascade_delete=True) images: list[Image] | None = Relationship(back_populates="product_type", cascade_delete=True) diff --git a/backend/app/api/data_collection/models.py b/backend/app/api/data_collection/models.py index 0e67d97..4df8c42 100644 --- a/backend/app/api/data_collection/models.py +++ b/backend/app/api/data_collection/models.py @@ -58,7 +58,7 @@ class PhysicalProperties(PhysicalPropertiesBase, TimeStampMixinBare, table=True) # One-to-one relationships product_id: int = Field(foreign_key="product.id") - product: "Product" = Relationship(back_populates="physical_properties") + product: Product = Relationship(back_populates="physical_properties") class CircularityPropertiesBase(CustomBase): @@ -87,7 +87,7 @@ class CircularityProperties(CircularityPropertiesBase, TimeStampMixinBare, table # One-to-one relationships product_id: int = Field(foreign_key="product.id") - product: "Product" = Relationship(back_populates="circularity_properties") + product: Product = Relationship(back_populates="circularity_properties") ### Product Model ### @@ -124,7 +124,7 @@ class Product(ProductBase, TimeStampMixinBare, table=True): # Self-referential relationship for hierarchy parent_id: int | None = Field(default=None, foreign_key="product.id") - parent: Optional["Product"] = Relationship( + parent: Product | None = Relationship( back_populates="components", sa_relationship_kwargs={ "uselist": False, @@ -134,7 +134,7 @@ class Product(ProductBase, TimeStampMixinBare, table=True): }, ) amount_in_parent: int | None = Field(default=None, description="Quantity within parent product") - components: list["Product"] | None = Relationship( + components: list[Product] | None = Relationship( back_populates="parent", cascade_delete=True, sa_relationship_kwargs={"lazy": "selectin", "join_depth": 1}, # Eagerly load linked parent product @@ -149,15 +149,15 @@ class Product(ProductBase, TimeStampMixinBare, table=True): ) # Many-to-one relationships - files: list["File"] | None = Relationship(back_populates="product", cascade_delete=True) - images: list["Image"] | None = Relationship( + files: list[File] | None = Relationship(back_populates="product", cascade_delete=True) + images: list[Image] | None = Relationship( back_populates="product", cascade_delete=True, sa_relationship_kwargs={"lazy": "subquery"} ) - videos: list["Video"] | None = Relationship(back_populates="product", cascade_delete=True) + videos: list[Video] | None = Relationship(back_populates="product", cascade_delete=True) # One-to-many relationships owner_id: UUID4 = Field(foreign_key="user.id") - owner: "User" = Relationship( + owner: User = Relationship( back_populates="products", sa_relationship_kwargs={ "uselist": False, @@ -168,7 +168,7 @@ class Product(ProductBase, TimeStampMixinBare, table=True): ) product_type_id: int | None = Field(default=None, foreign_key="producttype.id") - product_type: "ProductType" = Relationship(back_populates="products", sa_relationship_kwargs={"uselist": False}) + product_type: ProductType = Relationship(back_populates="products", sa_relationship_kwargs={"uselist": False}) # Many-to-many relationships bill_of_materials: list[MaterialProductLink] | None = Relationship( @@ -194,7 +194,7 @@ def has_cycles(self) -> bool: """Check if the product hierarchy contains cycles.""" visited = set() - def visit(node: "Product") -> bool: + def visit(node: Product) -> bool: if node.id in visited: return True # Cycle detected visited.add(node.id) @@ -210,7 +210,7 @@ def visit(node: "Product") -> bool: def components_resolve_to_materials(self) -> bool: """Ensure all leaf components have a non-empty bill of materials.""" - def check(node: "Product") -> bool: + def check(node: Product) -> bool: if not node.components: # Leaf node if not node.bill_of_materials: diff --git a/backend/app/api/file_storage/models/models.py b/backend/app/api/file_storage/models/models.py index d69fa48..d159d99 100644 --- a/backend/app/api/file_storage/models/models.py +++ b/backend/app/api/file_storage/models/models.py @@ -65,13 +65,13 @@ class File(FileBase, TimeStampMixinBare, SingleParentMixin[FileParentType], tabl ) product_id: int | None = Field(default=None, foreign_key="product.id") - product: "Product" = Relationship(back_populates="files") + product: Product = Relationship(back_populates="files") material_id: int | None = Field(default=None, foreign_key="material.id") - material: "Material" = Relationship(back_populates="files") + material: Material = Relationship(back_populates="files") product_type_id: int | None = Field(default=None, foreign_key="producttype.id") - product_type: "ProductType" = Relationship(back_populates="files") + product_type: ProductType = Relationship(back_populates="files") # Model configuration model_config: ConfigDict = ConfigDict(arbitrary_types_allowed=True, use_enum_values=True) # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 @@ -129,13 +129,13 @@ class Image(ImageBase, TimeStampMixinBare, SingleParentMixin, table=True): ) product_id: int | None = Field(default=None, foreign_key="product.id") - product: "Product" = Relationship(back_populates="images") + product: Product = Relationship(back_populates="images") material_id: int | None = Field(default=None, foreign_key="material.id") - material: "Material" = Relationship(back_populates="images") + material: Material = Relationship(back_populates="images") product_type_id: int | None = Field(default=None, foreign_key="producttype.id") - product_type: "ProductType" = Relationship(back_populates="images") + product_type: ProductType = Relationship(back_populates="images") # Model configuration model_config: ConfigDict = ConfigDict(arbitrary_types_allowed=True) # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 diff --git a/backend/app/api/plugins/rpi_cam/models.py b/backend/app/api/plugins/rpi_cam/models.py index fae5de8..fc8dc46 100644 --- a/backend/app/api/plugins/rpi_cam/models.py +++ b/backend/app/api/plugins/rpi_cam/models.py @@ -84,7 +84,7 @@ class Camera(CameraBase, TimeStampMixinBare, table=True): # Many-to-one relationship with User owner_id: UUID4 = Field(foreign_key="user.id") - owner: "User" = Relationship( # One-way relationship to maintain plugin isolation + owner: User = Relationship( # One-way relationship to maintain plugin isolation sa_relationship_kwargs={ "primaryjoin": "Camera.owner_id == User.id", "foreign_keys": "[Camera.owner_id]", diff --git a/backend/app/main.py b/backend/app/main.py index 88c4e9e..c42a6b9 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,28 +1,29 @@ """Main application module for the Reverse Engineering Lab - Data collection API. This module initializes the FastAPI application, sets up the API routes, -mounts static and upload directories, and initializes the admin interface. +and mounts static and upload directories. """ import logging -from collections.abc import AsyncGenerator from contextlib import asynccontextmanager +from typing import TYPE_CHECKING from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from fastapi_pagination import add_pagination -from app.api.admin.main import init_admin from app.api.auth.utils.email_validation import EmailChecker from app.api.common.routers.exceptions import register_exception_handlers from app.api.common.routers.main import router from app.api.common.routers.openapi import init_openapi_docs from app.core.config import settings -from app.core.database import async_engine from app.core.logging import setup_logging from app.core.redis import close_redis, init_redis +if TYPE_CHECKING: + from collections.abc import AsyncGenerator + # Initialize logging setup_logging() logger = logging.getLogger(__name__) @@ -94,9 +95,6 @@ async def lifespan(app: FastAPI) -> AsyncGenerator: # Initialize OpenAPI documentation init_openapi_docs(app) -# Initialize admin interface -admin = init_admin(app, async_engine) - # Mount local file storage app.mount("/uploads", StaticFiles(directory=settings.uploads_path), name="uploads") app.mount("/static", StaticFiles(directory=settings.static_files_path), name="static") diff --git a/backend/app/templates/index.html b/backend/app/templates/index.html index 7ddf4a8..005923a 100644 --- a/backend/app/templates/index.html +++ b/backend/app/templates/index.html @@ -25,13 +25,6 @@

API Documentation

{% endif %} - {% if show_full_docs %} -
-

Administration

- Admin Dashboard -
- {% endif %} -

API Login

{% if user %} diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 9c31527..24aa274 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -79,8 +79,7 @@ "google-auth>=2.40.3", "itsdangerous>=2.2.0", "markupsafe >=3.0.2", - "sqladmin >=0.20.1", - ] +] migrations = ["alembic >=1.16.2", "alembic-postgresql-enum >=1.7.0", "openpyxl>=3.1.5", "pandas>=2.3.3"] diff --git a/backend/uv.lock b/backend/uv.lock index 2f9bde6..03d51ab 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -1927,7 +1927,6 @@ api = [ { name = "google-auth" }, { name = "itsdangerous" }, { name = "markupsafe" }, - { name = "sqladmin" }, ] dev = [ { name = "alembic-autogen-check" }, @@ -1985,7 +1984,6 @@ api = [ { name = "google-auth", specifier = ">=2.40.3" }, { name = "itsdangerous", specifier = ">=2.2.0" }, { name = "markupsafe", specifier = ">=3.0.2" }, - { name = "sqladmin", specifier = ">=0.20.1" }, ] dev = [ { name = "alembic-autogen-check", specifier = ">=1.1.1" }, @@ -2226,22 +2224,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" }, ] -[[package]] -name = "sqladmin" -version = "0.22.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "python-multipart" }, - { name = "sqlalchemy" }, - { name = "starlette" }, - { name = "wtforms" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2c/ac/526bb3ff2dd94fbf8442bccb49ef40aa360045add19d4fbffcb43995e67a/sqladmin-0.22.0.tar.gz", hash = "sha256:4ea904d97e4d030edb68fb0681330b4d963f422442a64bee487fdc46119b3729", size = 1429937, upload-time = "2025-11-24T12:52:59.285Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/b4/ab78c7d7b13bd3f90d6d8a106c5ad12bf7a738f89eb0241b24ad8efe5d1e/sqladmin-0.22.0-py3-none-any.whl", hash = "sha256:f2fb11165a70601a97f71956104b47da2c432db49b0d7966dc65e9e6343887d3", size = 1445514, upload-time = "2025-11-24T12:53:00.511Z" }, -] - [[package]] name = "sqlalchemy" version = "2.0.44" @@ -2506,15 +2488,3 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, ] - -[[package]] -name = "wtforms" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6a/c7/96d10183c3470f1836846f7b9527d6cb0b6c2226ebca40f36fa29f23de60/wtforms-3.1.2.tar.gz", hash = "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9", size = 134705, upload-time = "2024-01-06T07:52:41.075Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/19/c3232f35e24dccfad372e9f341c4f3a1166ae7c66e4e1351a9467c921cc1/wtforms-3.1.2-py3-none-any.whl", hash = "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07", size = 145961, upload-time = "2024-01-06T07:52:43.023Z" }, -] diff --git a/docs/docs/architecture/system-design.md b/docs/docs/architecture/system-design.md index 0a2c523..c1cb781 100644 --- a/docs/docs/architecture/system-design.md +++ b/docs/docs/architecture/system-design.md @@ -7,11 +7,9 @@ The Reverse Engineering Lab platform is designed as a modular application for co ```mermaid graph TD User["User fa:fa-user"] -->|Interacts with| Frontend[Expo UI fa:fa-mobile] - SuperUser["Superuser fa:fa-user-shield"] -->|Interacts with| SQLAdmin[SQL Admin fa:fa-database] %% Core backend and DB Frontend -->|API Requests fa:fa-arrow-right| Backend[FastAPI Backend ] - SQLAdmin -->|Interfaces with fa:fa-link| Backend Backend -->|Queries fa:fa-database| PostgreSQL[(PostgreSQL )] %% Authentication @@ -36,7 +34,6 @@ graph TD style Frontend fill:#e0f7fa,stroke:#00acc1,stroke-width:2px style Backend fill:#e8f5e9,stroke:#4caf50,stroke-width:2px style PostgreSQL fill:#bbdefb,stroke:#1976d2,stroke-width:2px; - style SQLAdmin fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px style RaspberryPi fill:#f8bbd0,stroke:#e91e63,stroke-width:2px; style YouTube fill:#ffe6e6,stroke:#ff0000,stroke-width:2px; style Alembic fill:#fce4ec,stroke:#f06292,stroke-width:2px @@ -53,7 +50,6 @@ graph TD ## Technology Stack - **Backend**: [FastAPI](https://fastapi.tiangolo.com/) -- **Admin interface**: [SQLAdmin](https://github.com/aminalaee/sqladmin) - **ORM layer**: [SQLModel](https://github.com/fastapi/sqlmodel) - **Migrations**: [Alembic](https://alembic.sqlalchemy.org/en/latest/) - **Database**: [PostgreSQL](https://www.postgresql.org/) diff --git a/frontend-app/src/app/products/[id]/camera.tsx b/frontend-app/src/app/products/[id]/camera.tsx index d0f5b9e..d049252 100644 --- a/frontend-app/src/app/products/[id]/camera.tsx +++ b/frontend-app/src/app/products/[id]/camera.tsx @@ -1,96 +1,213 @@ -// MAIN camera.tsx import AsyncStorage from '@react-native-async-storage/async-storage'; -import { CameraView } from 'expo-camera'; +import { CameraView, useCameraPermissions } from 'expo-camera'; +import * as ImagePicker from 'expo-image-picker'; import { useLocalSearchParams, useRouter } from 'expo-router'; -import { useRef, useState } from 'react'; -import { Pressable, View } from 'react-native'; +import React from 'react'; +import { Platform, StyleSheet, View } from 'react-native'; +import { Button, Text } from 'react-native-paper'; import { processImage } from '@/services/media/imageProcessing'; import { useDialog } from '@/components/common/DialogProvider'; -type searchParams = { - id: string; -}; +type searchParams = { id: string }; export default function ProductCamera() { // Hooks const router = useRouter(); const dialog = useDialog(); const { id } = useLocalSearchParams(); - const ref = useRef(null); - - // States - const [ready, setReady] = useState(false); - const [cameraReady, setCameraReady] = useState(false); - - // Callbacks - const takePicture = async () => { - // Take picture - const photo = await ref.current?.takePictureAsync(); - if (!photo) return; - - // Process the image - const processedUri = await processImage(photo, { - onError: (error) => { - dialog.alert({ - title: error.type === 'size' ? 'Image too large' : 'Processing failed', - message: error.message, + + // ImagePicker permissions (mobile + mobile web) + const [cameraStatus, requestCameraPermission] = ImagePicker.useCameraPermissions(); + const [libraryStatus, requestLibraryPermission] = ImagePicker.useMediaLibraryPermissions(); + + // expo-camera permission (desktop web webcam) + const [webCamPermission, requestWebCamPermission] = useCameraPermissions(); + const camRef = React.useRef(null); + + // Detect desktop web (mouse/trackpad pointer) + const isDesktopWeb = + Platform.OS === 'web' && typeof window !== 'undefined' && !window.matchMedia('(pointer: coarse)').matches; + + const handleImageResult = async (result: ImagePicker.ImagePickerResult) => { + console.log('ImagePicker result:', result); + if (!result.canceled && result.assets?.[0]) { + try { + const processedUri = await processImage(result.assets[0], { + onError: (error) => { + dialog.alert({ + title: error.type === 'size' ? 'Image too large' : 'Processing failed', + message: error.message, + }); + }, }); - }, - }); - if (!processedUri) { + if (processedUri) { + await handleCapturedUri(processedUri); + } else { + router.back(); + } + } catch (error) { + console.error('Failed to process image:', error); + router.back(); + } + } else { + console.log('Image picking canceled'); router.back(); - return; } + }; - // Save photo URI to AsyncStorage - await AsyncStorage.setItem('lastPhoto', processedUri); - - // Dismiss and return to product page + const handleCapturedUri = async (uri: string) => { + console.log('Captured URI:', uri); + await AsyncStorage.setItem('lastPhoto', uri); const params = { id: id, photoTaken: 'taken' }; router.dismissTo({ pathname: '/products/[id]', params: params }); }; - // Render - return ( - setReady(true)}> - {/*Only render when layouting is done to fix visual bug on Android*/} - {ready && setCameraReady(true)} />} + const ensureWebcamPermission = async () => { + if (!webCamPermission?.granted) { + const p = await requestWebCamPermission(); + if (!p.granted) { + await dialog.alert({ + title: 'Permission Required', + message: 'Camera permission is required to take photos', + }); + return false; + } + } + return true; + }; - {/*Only enable taking pictures when camera is actually ready*/} - {cameraReady && } - - ); -} + const takePhoto = async () => { + console.log('takePhoto pressed. isDesktopWeb:', isDesktopWeb); + + if (isDesktopWeb) { + // Desktop web: Capture from webcam + const ok = await ensureWebcamPermission(); + if (!ok) return; + try { + const photo = await camRef.current?.takePictureAsync(); + if (photo?.uri) { + // Process the webcam photo through the same validation + const processedUri = await processImage(photo, { + onError: (error) => { + dialog.alert({ + title: error.type === 'size' ? 'Image too large' : 'Processing failed', + message: error.message, + }); + }, + }); + + if (processedUri) { + await handleCapturedUri(processedUri); + } + } else { + console.warn('No photo URI returned from webcam'); + } + } catch (e) { + console.error('Webcam capture error:', e); + } + return; + } + + // Mobile / mobile web: Use ImagePicker camera + if (cameraStatus?.status !== 'granted') { + const permission = await requestCameraPermission(); + if (!permission.granted) { + await dialog.alert({ + title: 'Permission Required', + message: 'Camera permission is required to take photos', + }); + return; + } + } + + try { + const result = await ImagePicker.launchCameraAsync({ + allowsEditing: true, + mediaTypes: 'images', + }); + await handleImageResult(result); + } catch (error: any) { + console.error('Camera error:', error); + if (error.message?.includes('Unsupported file type')) { + await dialog.alert({ + title: 'Unsupported file', + message: 'Please select an image file.', + }); + } + } + }; + + const pickFromGallery = async () => { + console.log('pickFromGallery pressed'); + if (libraryStatus?.status !== 'granted') { + const permission = await requestLibraryPermission(); + if (!permission.granted) { + await dialog.alert({ + title: 'Permission Required', + message: 'Media library permission is required to choose photos', + }); + return; + } + } + try { + const result = await ImagePicker.launchImageLibraryAsync({ + allowsEditing: true, + mediaTypes: 'images', + }); + await handleImageResult(result); + } catch (error: any) { + console.error('Gallery picker error:', error); + if (error.message?.includes('Unsupported file type')) { + await dialog.alert({ + title: 'Unsupported file', + message: 'Please select an image file. PDFs and other documents are not supported.', + }); + } + } + }; -function CameraButton({ onPress }: { onPress: () => void }) { - // Render return ( - - {({ pressed }) => ( - - + + + Add Product Image + + + {isDesktopWeb && ( + + {webCamPermission?.granted ? ( + + ) : ( + + + Allow camera access to take a photo + + + + )} )} - + + + + + + + ); } + +const styles = StyleSheet.create({ + header: { alignItems: 'center', justifyContent: 'center', paddingVertical: 16 }, + permissionBox: { flex: 1, alignItems: 'center', justifyContent: 'center', padding: 20 }, + actions: { alignItems: 'center', justifyContent: 'center', padding: 16, gap: 8 }, + button: { marginVertical: 6, minWidth: 200 }, +}); diff --git a/frontend-app/src/services/api/saving.ts b/frontend-app/src/services/api/saving.ts index e9ec3d5..364bd56 100644 --- a/frontend-app/src/services/api/saving.ts +++ b/frontend-app/src/services/api/saving.ts @@ -175,13 +175,15 @@ async function addImage(product: Product, image: { url: string; description: str } else if (image.url.startsWith('file:')) { console.log(image.url); body.append('file', { uri: image.url, name: 'image.png', type: 'image/png' } as any); - } else if (image.url.startsWith('blob:')) { - // Fetch the blob from the blob URL + } else if (image.url.startsWith('blob:') || image.url.startsWith('http')) { + // Web blob or URL - fetch and convert to blob const response = await fetch(image.url); const blob = await response.blob(); body.append('file', blob, 'image.png'); } + console.log('[AddImage] Uploading image:', image.url); + await fetch(url, { method: 'POST', headers: headers, body: body }); } From c4eb2be1f9f3c3f40a7a64de257757feed5b07b2 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 8 Dec 2025 11:00:23 +0000 Subject: [PATCH 68/79] fix(backend): Remove min length constraints from CircularityProperties Patch update schema --- backend/app/api/data_collection/schemas.py | 6 +++--- .../api/plugins/rpi_cam/routers/camera_interaction/utils.py | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/backend/app/api/data_collection/schemas.py b/backend/app/api/data_collection/schemas.py index 0471fa9..03a9d6a 100644 --- a/backend/app/api/data_collection/schemas.py +++ b/backend/app/api/data_collection/schemas.py @@ -143,9 +143,9 @@ class CircularityPropertiesUpdate(BaseUpdateSchema, CircularityPropertiesBase): """Schema for updating circularity properties.""" # Make all fields optional for updates - recyclability_observation: str | None = Field(default=None, min_length=2, max_length=500) - repairability_observation: str | None = Field(default=None, min_length=2, max_length=500) - remanufacturability_observation: str | None = Field(default=None, min_length=2, max_length=500) + recyclability_observation: str | None = Field(default=None, max_length=500) + repairability_observation: str | None = Field(default=None, max_length=500) + remanufacturability_observation: str | None = Field(default=None, max_length=500) model_config: ConfigDict = ConfigDict( json_schema_extra={ diff --git a/backend/app/api/plugins/rpi_cam/routers/camera_interaction/utils.py b/backend/app/api/plugins/rpi_cam/routers/camera_interaction/utils.py index 23f8f5a..cd5de42 100644 --- a/backend/app/api/plugins/rpi_cam/routers/camera_interaction/utils.py +++ b/backend/app/api/plugins/rpi_cam/routers/camera_interaction/utils.py @@ -2,6 +2,7 @@ import logging from enum import Enum +from typing import TYPE_CHECKING from urllib.parse import urljoin from fastapi import HTTPException @@ -12,6 +13,10 @@ from app.api.common.utils import get_user_owned_object from app.api.plugins.rpi_cam.models import Camera, CameraConnectionStatus +if TYPE_CHECKING: + from pydantic import UUID4 + from sqlmodel.ext.asyncio.session import AsyncSession + class HttpMethod(str, Enum): """HTTP method type.""" From 88a6cbcc203b2078e9b27585103f41bf9f9be76c Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 8 Dec 2025 12:12:37 +0100 Subject: [PATCH 69/79] fix(backend): Hotfix for SQLmodel issues --- backend/app/api/auth/models.py | 4 +- backend/app/api/background_data/models.py | 2 +- backend/app/api/common/crud/utils.py | 2 + backend/app/api/data_collection/models.py | 2 +- backend/uv.lock | 351 +++++++++++----------- 5 files changed, 180 insertions(+), 181 deletions(-) diff --git a/backend/app/api/auth/models.py b/backend/app/api/auth/models.py index afe73c1..c804717 100644 --- a/backend/app/api/auth/models.py +++ b/backend/app/api/auth/models.py @@ -68,7 +68,7 @@ class User(SQLModelBaseUserDB, CustomBaseBare, UserBase, TimeStampMixinBare, tab nullable=True, ), ) - organization: Organization | None = Relationship( + organization: Optional[Organization] = Relationship( # noqa: UP045 # Using 'Optional' over Organization | None to avoid issues with sqlmodel type detection back_populates="members", sa_relationship_kwargs={ "lazy": "selectin", @@ -79,7 +79,7 @@ class User(SQLModelBaseUserDB, CustomBaseBare, UserBase, TimeStampMixinBare, tab organization_role: OrganizationRole | None = Field(default=None, sa_column=Column(SAEnum(OrganizationRole))) # One-to-one relationship with owned Organization - owned_organization: Organization | None = Relationship( + owned_organization: Optional[Organization] = Relationship( # noqa: UP045 # Using 'Optional' over Organization | None to avoid issues with sqlmodel type detection back_populates="owner", sa_relationship_kwargs={ "uselist": False, diff --git a/backend/app/api/background_data/models.py b/backend/app/api/background_data/models.py index 1600452..2ee8f92 100644 --- a/backend/app/api/background_data/models.py +++ b/backend/app/api/background_data/models.py @@ -89,7 +89,7 @@ class Category(CategoryBase, TimeStampMixinBare, table=True): # Self-referential relationship supercategory_id: int | None = Field(foreign_key="category.id", default=None, nullable=True) - supercategory: Category | None = Relationship( + supercategory: Optional[Category] = Relationship( # noqa: UP045 # Using 'Optional' over Category | None to avoid issues with sqlmodel type detection back_populates="subcategories", sa_relationship_kwargs={"remote_side": "Category.id", "lazy": "selectin", "join_depth": 1}, ) diff --git a/backend/app/api/common/crud/utils.py b/backend/app/api/common/crud/utils.py index 8d051bd..c223a53 100644 --- a/backend/app/api/common/crud/utils.py +++ b/backend/app/api/common/crud/utils.py @@ -47,6 +47,8 @@ def add_relationship_options( """ # Get all relationships from the database model in one pass inspector: Mapper[Any] = inspect(model, raiseerr=True) + # HACK: Using SQLAlchemy internals to get relationship info. This sometimes causes runtime issues with circular model definitions. + # TODO: Fix this by finding a better way to get relationship info without using internals. all_db_rels = {rel.key: (getattr(model, rel.key), rel.uselist) for rel in inspector.relationships} # Determine which relationships are in scope (db ∩ schema) diff --git a/backend/app/api/data_collection/models.py b/backend/app/api/data_collection/models.py index 4df8c42..1261a22 100644 --- a/backend/app/api/data_collection/models.py +++ b/backend/app/api/data_collection/models.py @@ -124,7 +124,7 @@ class Product(ProductBase, TimeStampMixinBare, table=True): # Self-referential relationship for hierarchy parent_id: int | None = Field(default=None, foreign_key="product.id") - parent: Product | None = Relationship( + parent: Optional[Product] = Relationship( # noqa: UP045 # Using 'Optional' over Product | None to avoid issues with sqlmodel type detection back_populates="components", sa_relationship_kwargs={ "uselist": False, diff --git a/backend/uv.lock b/backend/uv.lock index 03d51ab..b05fc7c 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -75,15 +75,14 @@ wheels = [ [[package]] name = "anyio" -version = "4.11.0" +version = "4.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, - { name = "sniffio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } +sdist = { url = "https://files.pythonhosted.org/packages/16/ce/8a777047513153587e5434fd752e89334ac33e379aa3497db860eeb60377/anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0", size = 228266, upload-time = "2025-11-28T23:37:38.911Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9c/36c5c37947ebfb8c7f22e0eb6e4d188ee2d53aa3880f3f2744fb894f0cb1/anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb", size = 113362, upload-time = "2025-11-28T23:36:57.897Z" }, ] [[package]] @@ -225,15 +224,15 @@ wheels = [ [[package]] name = "beautifulsoup4" -version = "4.14.2" +version = "4.14.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "soupsieve" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822, upload-time = "2025-09-29T10:05:42.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392, upload-time = "2025-09-29T10:05:43.771Z" }, + { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, ] [[package]] @@ -247,30 +246,30 @@ wheels = [ [[package]] name = "boto3" -version = "1.41.3" +version = "1.42.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/30/1f1bfb34a97709b5d004d5c16ccac81a73ea6c5ce86ce75eaff0a75aee3f/boto3-1.41.3.tar.gz", hash = "sha256:8a89f3900a356879022c1600f72cbb3d8b85708f094d2d08a461bd193d0b07ca", size = 111614, upload-time = "2025-11-24T20:22:33.007Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/31/246916eec4fc5ff7bebf7e75caf47ee4d72b37d4120b6943e3460956e618/boto3-1.42.4.tar.gz", hash = "sha256:65f0d98a3786ec729ba9b5f70448895b2d1d1f27949aa7af5cb4f39da341bbc4", size = 112826, upload-time = "2025-12-05T20:27:14.931Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/d3/56e8c147e369fdc1b5526584f87151ca1742949bf5e6ab7500d926107624/boto3-1.41.3-py3-none-any.whl", hash = "sha256:10a3f5a72e071c362f5aa8443bd949edc31b7494c48a315ccdab14b1c387a1fd", size = 139345, upload-time = "2025-11-24T20:22:30.601Z" }, + { url = "https://files.pythonhosted.org/packages/00/25/9ae819385aad79f524859f7179cecf8ac019b63ac8f150c51b250967f6db/boto3-1.42.4-py3-none-any.whl", hash = "sha256:0f4089e230d55f981d67376e48cefd41c3d58c7f694480f13288e6ff7b1fefbc", size = 140621, upload-time = "2025-12-05T20:27:12.803Z" }, ] [[package]] name = "botocore" -version = "1.41.3" +version = "1.42.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/e9/d6207e08f35280cb8755b316f0e0a0cd2e8405d1b849e847c26fb4e3e3a6/botocore-1.41.3.tar.gz", hash = "sha256:1c6ad338f445c9bf02e231bfa302239d60520ec6dd88ded3206b34dca100103c", size = 14658770, upload-time = "2025-11-24T20:22:21.929Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/b7/dec048c124619b2702b5236c5fc9d8e5b0a87013529e9245dc49aaaf31ff/botocore-1.42.4.tar.gz", hash = "sha256:d4816023492b987a804f693c2d76fb751fdc8755d49933106d69e2489c4c0f98", size = 14848605, upload-time = "2025-12-05T20:27:02.919Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/18/a0597e4491d3a725768162c48a4dd1e1a57323fdb40fca04a34e9a68ef93/botocore-1.41.3-py3-none-any.whl", hash = "sha256:fe2379b30cc726e9e44bf47c3834fe208b85f7eaa57b934ab05f305ca9d05a8b", size = 14328009, upload-time = "2025-11-24T20:22:17.618Z" }, + { url = "https://files.pythonhosted.org/packages/9b/a2/7b50f12a9c5a33cd85a5f23fdf78a0cbc445c0245c16051bb627f328be06/botocore-1.42.4-py3-none-any.whl", hash = "sha256:c3b091fd33809f187824b6434e518b889514ded5164cb379358367c18e8b0d7d", size = 14519938, upload-time = "2025-12-05T20:26:58.881Z" }, ] [[package]] @@ -599,7 +598,7 @@ wheels = [ [[package]] name = "fastapi" -version = "0.122.0" +version = "0.124.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -607,9 +606,9 @@ dependencies = [ { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/de/3ee97a4f6ffef1fb70bf20561e4f88531633bb5045dc6cebc0f8471f764d/fastapi-0.122.0.tar.gz", hash = "sha256:cd9b5352031f93773228af8b4c443eedc2ac2aa74b27780387b853c3726fb94b", size = 346436, upload-time = "2025-11-24T19:17:47.95Z" } +sdist = { url = "https://files.pythonhosted.org/packages/48/9c/11969bd3e3bc4aa3a711f83dd3720239d3565a934929c74fc32f6c9f3638/fastapi-0.124.0.tar.gz", hash = "sha256:260cd178ad75e6d259991f2fd9b0fee924b224850079df576a3ba604ce58f4e6", size = 357623, upload-time = "2025-12-06T13:11:35.692Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/93/aa8072af4ff37b795f6bbf43dcaf61115f40f49935c7dbb180c9afc3f421/fastapi-0.122.0-py3-none-any.whl", hash = "sha256:a456e8915dfc6c8914a50d9651133bd47ec96d331c5b44600baa635538a30d67", size = 110671, upload-time = "2025-11-24T19:17:45.96Z" }, + { url = "https://files.pythonhosted.org/packages/4d/29/9e1e82e16e9a1763d3b55bfbe9b2fa39d7175a1fd97685c482fa402e111d/fastapi-0.124.0-py3-none-any.whl", hash = "sha256:91596bdc6dde303c318f06e8d2bc75eafb341fc793a0c9c92c0bc1db1ac52480", size = 112505, upload-time = "2025-12-06T13:11:34.392Z" }, ] [package.optional-dependencies] @@ -644,7 +643,7 @@ standard = [ [[package]] name = "fastapi-cloud-cli" -version = "0.5.1" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastar" }, @@ -656,9 +655,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cd/8d/cb1ae52121190eb75178b146652bfdce9296d2fd19aa30410ebb1fab3a63/fastapi_cloud_cli-0.5.1.tar.gz", hash = "sha256:5ed9591fda9ef5ed846c7fb937a06c491a00eef6d5bb656c84d82f47e500804b", size = 30746, upload-time = "2025-11-20T16:53:24.491Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/dd/e5890bb4ee63f9d8988660b755490e346cf5769aaa7f5f3ced9afb9f090a/fastapi_cloud_cli-0.6.0.tar.gz", hash = "sha256:2c333fff2e4b93b9efbec7896ce3ffa3e77ce4cf3d8cb14e35b4f823dfddac02", size = 30579, upload-time = "2025-12-04T15:04:07.008Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d6/b83f0801fd2c3f648e3696cdd2a1967b176f43c0c9db35c0350a67e7c141/fastapi_cloud_cli-0.5.1-py3-none-any.whl", hash = "sha256:1a28415b059b27af180a55a835ac2c9e924a66be88412d5649d4f91993d1a698", size = 23216, upload-time = "2025-11-20T16:53:23.119Z" }, + { url = "https://files.pythonhosted.org/packages/ac/2f/5ba9b5faa75067e30ff48e3c454263ebc2d2301d5509cfefe12cf9fc8156/fastapi_cloud_cli-0.6.0-py3-none-any.whl", hash = "sha256:b654890b5302c90d2f347b123a35186096328838a526316c470b6005cabd4983", size = 23215, upload-time = "2025-12-04T15:04:08.121Z" }, ] [[package]] @@ -693,16 +692,16 @@ dependencies = [ [[package]] name = "fastapi-pagination" -version = "0.15.0" +version = "0.15.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastapi" }, { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bc/db/8a3d097c491ad873574bd7295834ef89e16263ae9104855bbb5ee6d46e47/fastapi_pagination-0.15.0.tar.gz", hash = "sha256:11fe39cbe181ed3c18919b90faf6bfcbe40cb596aa9c52a98bbce85111a29a4f", size = 557472, upload-time = "2025-10-28T19:06:17.732Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/8e/3f9b6980123a7003ca0fb0696f20a4308380f704603a600e6f0425f778c4/fastapi_pagination-0.15.2.tar.gz", hash = "sha256:e804014e853773a7c036b2e97379e79aab6715b8cb883d8ae251697d97b25223", size = 571283, upload-time = "2025-12-06T13:54:20.002Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/91/b835e07234170ba85473227aa107bcf1dc616ff6cb643c0bd9b8225a55f1/fastapi_pagination-0.15.0-py3-none-any.whl", hash = "sha256:ffef937e78903fcb6f356b8407ec1fb0620a06675087fa7d0c4e537a60aa0447", size = 52292, upload-time = "2025-10-28T19:06:16.371Z" }, + { url = "https://files.pythonhosted.org/packages/ea/db/9430508035b18d905e898deb91be21afbad9c8016b59e29d13879e8100c8/fastapi_pagination-0.15.2-py3-none-any.whl", hash = "sha256:f8763a2e1caafe9c2a10ac860d6403935c31a067ffd6e93e57811bfec9873d87", size = 56106, upload-time = "2025-12-06T13:54:18.643Z" }, ] [[package]] @@ -767,55 +766,55 @@ dependencies = [ [[package]] name = "fastar" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d9/7e/0563141e374012f47eb0d219323378f4207d15d9939fa7aa0fa404d8613d/fastar-0.7.0.tar.gz", hash = "sha256:76739b48121cf8601ecc3ea9e87858362774b53cc1dd7e8332696b99c6ad2c27", size = 67917, upload-time = "2025-11-24T15:52:37.072Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/82/96043bd83b54f2074a7f47df7ad912b6de26b398a424580167a0d059b46e/fastar-0.7.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d66c09da9ed60536326783becab08db4d4f478e12c0543e7ac750336e72b38e5", size = 705365, upload-time = "2025-11-24T15:51:14.945Z" }, - { url = "https://files.pythonhosted.org/packages/66/01/24f42e7693713c41b389aaa15c0f010ac84eeb9dd5e4e2e0336386b2cef6/fastar-0.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4e443363617551be2e48f87a63f42ba1275c8f42094c6616168bd0512c9ed9b9", size = 627848, upload-time = "2025-11-24T15:51:00.295Z" }, - { url = "https://files.pythonhosted.org/packages/2e/5a/03d2589e2652506e73a8a85312852b5d3263ca348912fc39a396968009ff/fastar-0.7.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5a6981f162ebf1148c08668e1ab0fa58f4a6b32a0a126545042a859d836e54ec", size = 867646, upload-time = "2025-11-24T15:50:30.874Z" }, - { url = "https://files.pythonhosted.org/packages/dd/81/ac6f2484f8919b642a45088d487089ac926f74d9b12f347e4ed2e3ebaf8e/fastar-0.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7605ce63582432f2bc6b5e59e569b818f5db74506d452be609537a5699cedc19", size = 763982, upload-time = "2025-11-24T15:49:31.069Z" }, - { url = "https://files.pythonhosted.org/packages/eb/77/0ab5991e97e882a90043f287ba08124b8b0a2af4e68e3e8e77cb6e9b09ab/fastar-0.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9ae8c4dec44bac4a3e763d5993191962db1285525da61154b6bc158ebcd01ba4", size = 763680, upload-time = "2025-11-24T15:49:46.938Z" }, - { url = "https://files.pythonhosted.org/packages/b3/b4/0c269f4136278e0c652f7d6eca57e71104d02ba1fc3ebf7057a6c36e8339/fastar-0.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:abe4ff6fcc353618e395cceb760ae3a90d19686c2d67c9d6654ec0fa9d265395", size = 930118, upload-time = "2025-11-24T15:50:01.681Z" }, - { url = "https://files.pythonhosted.org/packages/70/11/f62a4b652534a5e4f3303b4124e9ca55864f77de9f74588643332f4e5caf/fastar-0.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b54bbb9aa12b2c5550dfafedfe664088bc22a8acc4eebcc9dff7a1ca3048216", size = 820641, upload-time = "2025-11-24T15:50:15.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/c6/669c167472d31ea94caa5afa75227ef6f123e3be8474f56f9dad01c9b8d8/fastar-0.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f434f0a91235aec22a1d39714af3283ef768bb2de548e61ee4f3a74fb3504a2e", size = 820106, upload-time = "2025-11-24T15:50:45.978Z" }, - { url = "https://files.pythonhosted.org/packages/1d/7a/305c99ff3708fc3cb6bebbc2f6469d3c3c4f51119306691d0f57283da0d2/fastar-0.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:400e48ca94e5ed9a1f4d17dd8e74cbd9a978de4ba718f5610c73ba6172dcc59b", size = 985425, upload-time = "2025-11-24T15:51:31.58Z" }, - { url = "https://files.pythonhosted.org/packages/c7/c5/04ab4db328d0e3193cf9b1bbc3147f98cf09e1f99c24906789b929198fa8/fastar-0.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:94b11ba3d9d23fe612a4a612a62d7b2f18e2d7a1be2d5f94b938448a906436e9", size = 1038104, upload-time = "2025-11-24T15:51:49.085Z" }, - { url = "https://files.pythonhosted.org/packages/e6/72/e7c7d684efe1b92062096c29d0d5b38ca549beb5eb35336acf212a90ddc8/fastar-0.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9610f6edb6fdb627491148e7071f725b4abffb8655554cad6a45637772f0795a", size = 1044294, upload-time = "2025-11-24T15:52:06.47Z" }, - { url = "https://files.pythonhosted.org/packages/e6/11/b2ad21f1b8ac20b6c4676e83f2dd3c5f70ff9a9926df60c3f4e36be8be08/fastar-0.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:db2373ebe1a699ce3ea34296ab85a22a572667aefd198ca6fa32fee5e69970fc", size = 993265, upload-time = "2025-11-24T15:52:24.049Z" }, - { url = "https://files.pythonhosted.org/packages/03/38/d44a7ea41c407d46c56f160fb870536e1dd9ba01c44b46d7091835ff1719/fastar-0.7.0-cp313-cp313-win32.whl", hash = "sha256:bcb4f04daa574108092abfba8c0f747e65910464671d5ab72e6f55d19f7e2a71", size = 455032, upload-time = "2025-11-24T15:53:03.244Z" }, - { url = "https://files.pythonhosted.org/packages/9d/65/d86c8d53b4f00bb7eed9c89eda2801d33930a8729dac72838807eb2d7314/fastar-0.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:a577121830ba14acd70a8eccc7a0f815a78e9f01981bc9b71a005caa08f63afa", size = 489446, upload-time = "2025-11-24T15:52:50.877Z" }, - { url = "https://files.pythonhosted.org/packages/04/6d/12bc62cd7a425747efbba0755cbfd23015d592c3bf85753442ff1283bfc6/fastar-0.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:b4e0ddd1fb513eac866eca22323dd28b2671aaa3facd10a854d3beef4933372b", size = 460203, upload-time = "2025-11-24T15:52:41.739Z" }, - { url = "https://files.pythonhosted.org/packages/3f/a5/a5eff2a7fe21026cce5fa3a175d88a23a34bca461cddeab87042c2c47e82/fastar-0.7.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:7cc47eeac659fed55f547b6c84fbd302726fab64de720c96d3ddcf0952535d0e", size = 705379, upload-time = "2025-11-24T15:51:16.497Z" }, - { url = "https://files.pythonhosted.org/packages/00/06/67228a6e1b32414afe79510ba1256b791541b8801d12660d6fbb203c88b7/fastar-0.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f3139c8d48bdb2c2d79a42eb940efc20e67e1b9dd26798257b71f0d9f0083a5a", size = 627905, upload-time = "2025-11-24T15:51:01.523Z" }, - { url = "https://files.pythonhosted.org/packages/ea/11/753fd5b766d5b170d6d47ebb31aee87b95f5e5776e2661132aae68cae51a/fastar-0.7.0-cp314-cp314-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f0e2c86b690116f50bd40c444fce6da000695e558a94e460d8b46eff6f23b26f", size = 868266, upload-time = "2025-11-24T15:50:32.119Z" }, - { url = "https://files.pythonhosted.org/packages/40/66/70a191f4d61df4bcda77e759bb840d3cdda796ff26628a454ca44ef58158/fastar-0.7.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a698533c59125856e1c14978c589f933de312f066f2a15978f11030807ac535", size = 763815, upload-time = "2025-11-24T15:49:32.214Z" }, - { url = "https://files.pythonhosted.org/packages/d2/a0/72e7886ec7dd16e523522253ecf1862e422e43e3142de29052a562b6499d/fastar-0.7.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:240c546a20b6f8c1edfe0ab40ac6113cecea02380d6f59e6f9be3d1e079d0767", size = 763288, upload-time = "2025-11-24T15:49:48.082Z" }, - { url = "https://files.pythonhosted.org/packages/9c/b5/0d1cc3356bba8afad036e1808dc10ca76341cafd681a4479c98eb37d947f/fastar-0.7.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f37e415192a27980377c0a0859275f178bfcd54c3b972f2f273bee1276a75f1", size = 929296, upload-time = "2025-11-24T15:50:02.957Z" }, - { url = "https://files.pythonhosted.org/packages/59/79/21aa7f864e2e3a1e7244475f864cd82d34b86aac73b1f54c8eb32778c34e/fastar-0.7.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c865328d56525fc71441f848dcf3d9d20855f3f619c4dca99ecdd932c7e0160c", size = 820264, upload-time = "2025-11-24T15:50:16.91Z" }, - { url = "https://files.pythonhosted.org/packages/de/91/c576af124855de6ffbb48511625ff51653029ba0fde8d3ef6913cf0f968c/fastar-0.7.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a9e11313551a10032a6cd97c27434fde6a858794257d709040a7b351b586fe4", size = 819896, upload-time = "2025-11-24T15:50:47.264Z" }, - { url = "https://files.pythonhosted.org/packages/bf/f1/3b3ada104c1924f0a78bc66f89a1bca4957c26e7ad5befaaa2f4701af7bb/fastar-0.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f0532d5ef74d0262f998150a7a2e5d8e51f411d400f655c5a83eb8775fc8d5ab", size = 985552, upload-time = "2025-11-24T15:51:32.859Z" }, - { url = "https://files.pythonhosted.org/packages/c1/1f/1f6424bc8bc2cdc932b16670433b4368b09bf32872b9975c1c1cba02891e/fastar-0.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:008930f99c7602da1ec820b165724621df8d6ca327d8877bd46f3600c848aae0", size = 1038126, upload-time = "2025-11-24T15:51:50.93Z" }, - { url = "https://files.pythonhosted.org/packages/09/8e/f4c4db8de826ea9ff134c6bc9bf2aaf1fc977eac9153b3356f6d181a3149/fastar-0.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6965219b0dbb897557617400ef3a21601a08cfac0ba0e0dfcdbde19a13e0769d", size = 1044273, upload-time = "2025-11-24T15:52:08.061Z" }, - { url = "https://files.pythonhosted.org/packages/71/c6/b1af54e78ea288144bbb1e2e7b2ad56342285029bb2b68f84bf8c8713d70/fastar-0.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bcf277df3c357db68b422944aa3717aff6178c797c4c64711437a81fc2271552", size = 993779, upload-time = "2025-11-24T15:52:25.818Z" }, - { url = "https://files.pythonhosted.org/packages/7f/25/f3043ebd1e19bb262a0ff7a2f2a07945e5e912ace308202e0f89b1d7f96c/fastar-0.7.0-cp314-cp314-win32.whl", hash = "sha256:12cff2cc933e4a74e56c591b1dda06cdae23c0718d07cdb696701e3596a23c5e", size = 455711, upload-time = "2025-11-24T15:53:05.198Z" }, - { url = "https://files.pythonhosted.org/packages/f9/13/b691a58b3cb1567c95b60032009549ccebcefabeceb6c3c4a6a3bddf9253/fastar-0.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:99e7d8928b1d7092053e40d9132a246b4ed8156fa3cecad3def3ea5b2fd24027", size = 489799, upload-time = "2025-11-24T15:52:52.552Z" }, - { url = "https://files.pythonhosted.org/packages/14/0e/7c907f00cb71abc56b1dc3d4aaeaee85061feb955f014ac75af9933f7895/fastar-0.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:cedf4212173f502fc61883a76142ccad9d9cbd2b61f0704d36b7bf6a17df311d", size = 460748, upload-time = "2025-11-24T15:52:43.105Z" }, - { url = "https://files.pythonhosted.org/packages/d5/97/a4cc30a5a962fe23e0b21937fb99ca5a267aa6dee1e3dd72df853a758cb0/fastar-0.7.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8484b7c55d77874d272c236869855021376722d9c51ff5747ad8b42896b6c4df", size = 704853, upload-time = "2025-11-24T15:51:17.708Z" }, - { url = "https://files.pythonhosted.org/packages/0e/4e/02312660f6027f5ad2bb75e16ea5f2a9f89439e0a502c754b4d8eff0beb1/fastar-0.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:514947a8d057e111a9ffd5943ce740d4186f9084562b44cc9875fa39b1a2e109", size = 626773, upload-time = "2025-11-24T15:51:02.835Z" }, - { url = "https://files.pythonhosted.org/packages/61/c7/e04147583ca17fbe6970dc20083b2a38e2ffc2e4e4f76d4e7640c0dbfa49/fastar-0.7.0-cp314-cp314t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1b71a5eb92f0c730798896e512a75f96b267bfd610b1148a8348dbcd565dea6c", size = 867940, upload-time = "2025-11-24T15:50:33.402Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c1/8316762971c117b8043202d531320b3ebb740fc02bc5208e8a734e7d5b3c/fastar-0.7.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fce1bfa66ceb0e96b6eee89f9efb3250929df22fdfdab8a08735c09b50cfe0c", size = 762971, upload-time = "2025-11-24T15:49:33.406Z" }, - { url = "https://files.pythonhosted.org/packages/62/07/d394742e2892818d52f391d40d24d60ef9a214865fef4a9e55339022d990/fastar-0.7.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9632c25c6a85f5eab589437bc6bfbb5461f93b799882e3c750b6f86448ad9ede", size = 762796, upload-time = "2025-11-24T15:49:49.187Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7d/bb3ab1f10500c765833fc2c931d11e3fa2dae5e42e0451af759a89b5ef57/fastar-0.7.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c45e2422cca8fd3b5509edf8db44cceeb0d4eed3cc12d90d91d0e1ea08034258", size = 929810, upload-time = "2025-11-24T15:50:04.166Z" }, - { url = "https://files.pythonhosted.org/packages/0e/cb/5e42841f52a65b02796bae27a484c23375eabb07750c88face71d82e3717/fastar-0.7.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99836a00322c39689f7d9772662a7b5ee62b3ec1a344ad693f9c162226775039", size = 819858, upload-time = "2025-11-24T15:50:18.395Z" }, - { url = "https://files.pythonhosted.org/packages/0e/7e/e268246b4f38421c84bb42048311fe269feacd8e1d5a6cac48b0f64f8044/fastar-0.7.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcd2756c2ae9f1374619207b98d1143c9865910c9fecd094c8656b95c5a9a45b", size = 819585, upload-time = "2025-11-24T15:50:48.488Z" }, - { url = "https://files.pythonhosted.org/packages/50/1f/3d05285c98d3245944540aec77364618e0f508d0c4bbf311a7762b644c35/fastar-0.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3ced9eddb9adcf8b27361c180f6bdfbc8cb2e36479aa00e4e7e78c17c7768efc", size = 984526, upload-time = "2025-11-24T15:51:34.988Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e0/34c114c7016901cac190b18871212f7433871470d1ba1c92ed891ae7d85f/fastar-0.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:39ba9256790a13289f986c07c73bbc075647337008f1faea104e5e013a17ee70", size = 1037651, upload-time = "2025-11-24T15:51:52.286Z" }, - { url = "https://files.pythonhosted.org/packages/39/7e/371ddb9ed65733aa51370bf774234a142d315f841538c7af7fd959cc5c5e/fastar-0.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f445e1acb722e228364c2d8012e6be1b46502062e3638cbe5b98c7c2d6bebb72", size = 1044369, upload-time = "2025-11-24T15:52:10.031Z" }, - { url = "https://files.pythonhosted.org/packages/92/0f/0d6a9fab23ba227f79f2e728aef274daf8fe8148c7cbd58022b752af7aeb/fastar-0.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1e9b1e0cb44b0d43dae153d80e519b04aa0bc4c98240d4a2d85c7ede13b37aae", size = 993840, upload-time = "2025-11-24T15:52:27.41Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e2/df1c197e4bfca4c23114ab1251c70b70a9a7a427a1ab73bef2dd9750056a/fastar-0.7.0-cp314-cp314t-win32.whl", hash = "sha256:44956db52c2d6afa5a26a9d2c8e926eb55902a9151ab0ce0bfa3023479db4800", size = 454334, upload-time = "2025-11-24T15:53:09.556Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b0/e2b55bb0b521ac9abada459cd2bce8488b36525f913af536bf1dec90dc03/fastar-0.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:cfd514372850774e8651c4e98b2b81bba0ae00f2e1dfa666da89ea5e02d1e61a", size = 489047, upload-time = "2025-11-24T15:52:57.327Z" }, - { url = "https://files.pythonhosted.org/packages/f3/c1/ea150ccd09a6247a65e162596db393fb642ad92bf7d2af9f7e4ae58233da/fastar-0.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:96a366565662567ba1b7c1d2f72e02584575a33b220c361707e168270b68d4e4", size = 459525, upload-time = "2025-11-24T15:52:44.492Z" }, +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524, upload-time = "2025-11-26T02:36:00.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/a5/79ecba3646e22d03eef1a66fb7fc156567213e2e4ab9faab3bbd4489e483/fastar-0.8.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:a3253a06845462ca2196024c7a18f5c0ba4de1532ab1c4bad23a40b332a06a6a", size = 706112, upload-time = "2025-11-26T02:34:39.237Z" }, + { url = "https://files.pythonhosted.org/packages/0a/03/4f883bce878218a8676c2d7ca09b50c856a5470bb3b7f63baf9521ea6995/fastar-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5cbeb3ebfa0980c68ff8b126295cc6b208ccd81b638aebc5a723d810a7a0e5d2", size = 628954, upload-time = "2025-11-26T02:34:23.705Z" }, + { url = "https://files.pythonhosted.org/packages/4f/f1/892e471f156b03d10ba48ace9384f5a896702a54506137462545f38e40b8/fastar-0.8.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1c0d5956b917daac77d333d48b3f0f3ff927b8039d5b32d8125462782369f761", size = 868685, upload-time = "2025-11-26T02:33:53.077Z" }, + { url = "https://files.pythonhosted.org/packages/39/ba/e24915045852e30014ec6840446975c03f4234d1c9270394b51d3ad18394/fastar-0.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27b404db2b786b65912927ce7f3790964a4bcbde42cdd13091b82a89cd655e1c", size = 765044, upload-time = "2025-11-26T02:32:48.187Z" }, + { url = "https://files.pythonhosted.org/packages/14/2c/1aa11ac21a99984864c2fca4994e094319ff3a2046e7a0343c39317bd5b9/fastar-0.8.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0902fc89dcf1e7f07b8563032a4159fe2b835e4c16942c76fd63451d0e5f76a3", size = 764322, upload-time = "2025-11-26T02:33:03.859Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f0/4b91902af39fe2d3bae7c85c6d789586b9fbcf618d7fdb3d37323915906d/fastar-0.8.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:069347e2f0f7a8b99bbac8cd1bc0e06c7b4a31dc964fc60d84b95eab3d869dc1", size = 931016, upload-time = "2025-11-26T02:33:19.902Z" }, + { url = "https://files.pythonhosted.org/packages/c9/97/8fc43a5a9c0a2dc195730f6f7a0f367d171282cd8be2511d0e87c6d2dad0/fastar-0.8.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd135306f6bfe9a835918280e0eb440b70ab303e0187d90ab51ca86e143f70d", size = 821308, upload-time = "2025-11-26T02:33:34.664Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e9/058615b63a7fd27965e8c5966f393ed0c169f7ff5012e1674f21684de3ba/fastar-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d06d6897f43c27154b5f2d0eb930a43a81b7eec73f6f0b0114814d4a10ab38", size = 821171, upload-time = "2025-11-26T02:34:08.498Z" }, + { url = "https://files.pythonhosted.org/packages/ca/cf/69e16a17961570a755c37ffb5b5aa7610d2e77807625f537989da66f2a9d/fastar-0.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a922f8439231fa0c32b15e8d70ff6d415619b9d40492029dabbc14a0c53b5f18", size = 986227, upload-time = "2025-11-26T02:34:55.06Z" }, + { url = "https://files.pythonhosted.org/packages/fb/83/2100192372e59b56f4ace37d7d9cabda511afd71b5febad1643d1c334271/fastar-0.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a739abd51eb766384b4caff83050888e80cd75bbcfec61e6d1e64875f94e4a40", size = 1039395, upload-time = "2025-11-26T02:35:12.166Z" }, + { url = "https://files.pythonhosted.org/packages/75/15/cdd03aca972f55872efbb7cf7540c3fa7b97a75d626303a3ea46932163dc/fastar-0.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5a65f419d808b23ac89d5cd1b13a2f340f15bc5d1d9af79f39fdb77bba48ff1b", size = 1044766, upload-time = "2025-11-26T02:35:29.62Z" }, + { url = "https://files.pythonhosted.org/packages/3d/29/945e69e4e2652329ace545999334ec31f1431fbae3abb0105587e11af2ae/fastar-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7bb2ae6c0cce58f0db1c9f20495e7557cca2c1ee9c69bbd90eafd54f139171c5", size = 994740, upload-time = "2025-11-26T02:35:47.887Z" }, + { url = "https://files.pythonhosted.org/packages/4b/5d/dbfe28f8cd1eb484bba0c62e5259b2cf6fea229d6ef43e05c06b5a78c034/fastar-0.8.0-cp313-cp313-win32.whl", hash = "sha256:b28753e0d18a643272597cb16d39f1053842aa43131ad3e260c03a2417d38401", size = 455990, upload-time = "2025-11-26T02:36:28.502Z" }, + { url = "https://files.pythonhosted.org/packages/e1/01/e965740bd36e60ef4c5aa2cbe42b6c4eb1dc3551009238a97c2e5e96bd23/fastar-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:620e5d737dce8321d49a5ebb7997f1fd0047cde3512082c27dc66d6ac8c1927a", size = 490227, upload-time = "2025-11-26T02:36:14.363Z" }, + { url = "https://files.pythonhosted.org/packages/dd/10/c99202719b83e5249f26902ae53a05aea67d840eeb242019322f20fc171c/fastar-0.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:c4c4bd08df563120cd33e854fe0a93b81579e8571b11f9b7da9e84c37da2d6b6", size = 461078, upload-time = "2025-11-26T02:36:04.94Z" }, + { url = "https://files.pythonhosted.org/packages/96/4a/9573b87a0ef07580ed111e7230259aec31bb33ca3667963ebee77022ec61/fastar-0.8.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:50b36ce654ba44b0e13fae607ae17ee6e1597b69f71df1bee64bb8328d881dfc", size = 706041, upload-time = "2025-11-26T02:34:40.638Z" }, + { url = "https://files.pythonhosted.org/packages/4a/19/f95444a1d4f375333af49300aa75ee93afa3335c0e40fda528e460ed859c/fastar-0.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:63a892762683d7ab00df0227d5ea9677c62ff2cde9b875e666c0be569ed940f3", size = 628617, upload-time = "2025-11-26T02:34:24.893Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c9/b51481b38b7e3f16ef2b9e233b1a3623386c939d745d6e41bbd389eaae30/fastar-0.8.0-cp314-cp314-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4ae6a145c1bff592644bde13f2115e0239f4b7babaf506d14e7d208483cf01a5", size = 869299, upload-time = "2025-11-26T02:33:54.274Z" }, + { url = "https://files.pythonhosted.org/packages/bf/02/3ba1267ee5ba7314e29c431cf82eaa68586f2c40cdfa08be3632b7d07619/fastar-0.8.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ae0ff7c0a1c7e1428404b81faee8aebef466bfd0be25bfe4dabf5d535c68741", size = 764667, upload-time = "2025-11-26T02:32:49.606Z" }, + { url = "https://files.pythonhosted.org/packages/1b/84/bf33530fd015b5d7c2cc69e0bce4a38d736754a6955487005aab1af6adcd/fastar-0.8.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dbfd87dbd217b45c898b2dbcd0169aae534b2c1c5cbe3119510881f6a5ac8ef5", size = 763993, upload-time = "2025-11-26T02:33:05.782Z" }, + { url = "https://files.pythonhosted.org/packages/da/e0/9564d24e7cea6321a8d921c6d2a457044a476ef197aa4708e179d3d97f0d/fastar-0.8.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a5abd99fcba83ef28c8fe6ae2927edc79053db43a0457a962ed85c9bf150d37", size = 930153, upload-time = "2025-11-26T02:33:21.53Z" }, + { url = "https://files.pythonhosted.org/packages/35/b1/6f57fcd8d6e192cfebf97e58eb27751640ad93784c857b79039e84387b51/fastar-0.8.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91d4c685620c3a9d6b5ae091dbabab4f98b20049b7ecc7976e19cc9016c0d5d6", size = 821177, upload-time = "2025-11-26T02:33:35.839Z" }, + { url = "https://files.pythonhosted.org/packages/b3/78/9e004ea9f3aa7466f5ddb6f9518780e1d2f0ed3ca55f093632982598bace/fastar-0.8.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f77c2f2cad76e9dc7b6701297adb1eba87d0485944b416fc2ccf5516c01219a3", size = 820652, upload-time = "2025-11-26T02:34:09.776Z" }, + { url = "https://files.pythonhosted.org/packages/42/95/b604ed536544005c9f1aee7c4c74b00150db3d8d535cd8232dc20f947063/fastar-0.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e7f07c4a3dada7757a8fc430a5b4a29e6ef696d2212747213f57086ffd970316", size = 985961, upload-time = "2025-11-26T02:34:56.401Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7b/fa9d4d96a5d494bdb8699363bb9de8178c0c21a02e1d89cd6f913d127018/fastar-0.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:90c0c3fe55105c0aed8a83135dbdeb31e683455dbd326a1c48fa44c378b85616", size = 1039316, upload-time = "2025-11-26T02:35:13.807Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f9/8462789243bc3f33e8401378ec6d54de4e20cfa60c96a0e15e3e9d1389bb/fastar-0.8.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fb9ee51e5bffe0dab3d3126d3a4fac8d8f7235cedcb4b8e74936087ce1c157f3", size = 1045028, upload-time = "2025-11-26T02:35:31.079Z" }, + { url = "https://files.pythonhosted.org/packages/a5/71/9abb128777e616127194b509e98fcda3db797d76288c1a8c23dd22afc14f/fastar-0.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e380b1e8d30317f52406c43b11e98d11e1d68723bbd031e18049ea3497b59a6d", size = 994677, upload-time = "2025-11-26T02:35:49.391Z" }, + { url = "https://files.pythonhosted.org/packages/de/c1/b81b3f194853d7ad232a67a1d768f5f51a016f165cfb56cb31b31bbc6177/fastar-0.8.0-cp314-cp314-win32.whl", hash = "sha256:1c4ffc06e9c4a8ca498c07e094670d8d8c0d25b17ca6465b9774da44ea997ab1", size = 456687, upload-time = "2025-11-26T02:36:30.205Z" }, + { url = "https://files.pythonhosted.org/packages/cb/87/9e0cd4768a98181d56f0cdbab2363404cc15deb93f4aad3b99cd2761bbaa/fastar-0.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:5517a8ad4726267c57a3e0e2a44430b782e00b230bf51c55b5728e758bb3a692", size = 490578, upload-time = "2025-11-26T02:36:16.218Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1e/580a76cf91847654f2ad6520e956e93218f778540975bc4190d363f709e2/fastar-0.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:58030551046ff4a8616931e52a36c83545ff05996db5beb6e0cd2b7e748aa309", size = 461473, upload-time = "2025-11-26T02:36:06.373Z" }, + { url = "https://files.pythonhosted.org/packages/58/4c/bdb5c6efe934f68708529c8c9d4055ebef5c4be370621966438f658b29bd/fastar-0.8.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:1e7d29b6bfecb29db126a08baf3c04a5ab667f6cea2b7067d3e623a67729c4a6", size = 705570, upload-time = "2025-11-26T02:34:42.01Z" }, + { url = "https://files.pythonhosted.org/packages/6d/78/f01ac7e71d5a37621bd13598a26e948a12b85ca8042f7ee1a0a8c9f59cda/fastar-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:05eb7b96940f9526b485f1d0b02393839f0f61cac4b1f60024984f8b326d2640", size = 627761, upload-time = "2025-11-26T02:34:26.152Z" }, + { url = "https://files.pythonhosted.org/packages/06/45/6df0ecda86ea9d2e95053c1a655d153dee55fc121b6e13ea6d1e246a50b6/fastar-0.8.0-cp314-cp314t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:619352d8ac011794e2345c462189dc02ba634750d23cd9d86a9267dd71b1f278", size = 869414, upload-time = "2025-11-26T02:33:55.618Z" }, + { url = "https://files.pythonhosted.org/packages/b2/72/486421f5a8c0c377cc82e7a50c8a8ea899a6ec2aa72bde8f09fb667a2dc8/fastar-0.8.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74ebfecef3fe6d7a90355fac1402fd30636988332a1d33f3e80019a10782bb24", size = 763863, upload-time = "2025-11-26T02:32:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/d4/64/39f654dbb41a3867fb1f2c8081c014d8f1d32ea10585d84cacbef0b32995/fastar-0.8.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2975aca5a639e26a3ab0d23b4b0628d6dd6d521146c3c11486d782be621a35aa", size = 763065, upload-time = "2025-11-26T02:33:07.274Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bd/c011a34fb3534c4c3301f7c87c4ffd7e47f6113c904c092ddc8a59a303ea/fastar-0.8.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afc438eaed8ff0dcdd9308268be5cb38c1db7e94c3ccca7c498ca13a4a4535a3", size = 930530, upload-time = "2025-11-26T02:33:23.117Z" }, + { url = "https://files.pythonhosted.org/packages/55/9d/aa6e887a7033c571b1064429222bbe09adc9a3c1e04f3d1788ba5838ebd5/fastar-0.8.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ced0a5399cc0a84a858ef0a31ca2d0c24d3bbec4bcda506a9192d8119f3590a", size = 820572, upload-time = "2025-11-26T02:33:37.542Z" }, + { url = "https://files.pythonhosted.org/packages/ad/9c/7a3a2278a1052e1a5d98646de7c095a00cffd2492b3b84ce730e2f1cd93a/fastar-0.8.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec9b23da8c4c039da3fe2e358973c66976a0c8508aa06d6626b4403cb5666c19", size = 820649, upload-time = "2025-11-26T02:34:11.108Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/d38edc1f4438cd047e56137c26d94783ffade42e1b3bde620ccf17b771ef/fastar-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:dfba078fcd53478032fd0ceed56960ec6b7ff0511cfc013a8a3a4307e3a7bac4", size = 985653, upload-time = "2025-11-26T02:34:57.884Z" }, + { url = "https://files.pythonhosted.org/packages/69/d9/2147d0c19757e165cd62d41cec3f7b38fad2ad68ab784978b5f81716c7ea/fastar-0.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ade56c94c14be356d295fecb47a3fcd473dd43a8803ead2e2b5b9e58feb6dcfa", size = 1038140, upload-time = "2025-11-26T02:35:15.778Z" }, + { url = "https://files.pythonhosted.org/packages/7f/1d/ec4c717ffb8a308871e9602ec3197d957e238dc0227127ac573ec9bca952/fastar-0.8.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e48d938f9366db5e59441728f70b7f6c1ccfab7eff84f96f9b7e689b07786c52", size = 1045195, upload-time = "2025-11-26T02:35:32.865Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/637334dc8c8f3bb391388b064ae13f0ad9402bc5a6c3e77b8887d0c31921/fastar-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:79c441dc1482ff51a54fb3f57ae6f7bb3d2cff88fa2cc5d196c519f8aab64a56", size = 994686, upload-time = "2025-11-26T02:35:51.392Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e2/dfa19a4b260b8ab3581b7484dcb80c09b25324f4daa6b6ae1c7640d1607a/fastar-0.8.0-cp314-cp314t-win32.whl", hash = "sha256:187f61dc739afe45ac8e47ed7fd1adc45d52eac110cf27d579155720507d6fbe", size = 455767, upload-time = "2025-11-26T02:36:34.758Z" }, + { url = "https://files.pythonhosted.org/packages/51/47/df65c72afc1297797b255f90c4778b5d6f1f0f80282a134d5ab610310ed9/fastar-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:40e9d763cf8bf85ce2fa256e010aa795c0fe3d3bd1326d5c3084e6ce7857127e", size = 489971, upload-time = "2025-11-26T02:36:22.081Z" }, + { url = "https://files.pythonhosted.org/packages/85/11/0aa8455af26f0ae89e42be67f3a874255ee5d7f0f026fc86e8d56f76b428/fastar-0.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:e59673307b6a08210987059a2bdea2614fe26e3335d0e5d1a3d95f49a05b1418", size = 460467, upload-time = "2025-11-26T02:36:07.978Z" }, ] [[package]] @@ -900,30 +899,33 @@ wheels = [ [[package]] name = "greenlet" -version = "3.2.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" }, - { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" }, - { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191, upload-time = "2025-08-07T13:45:29.752Z" }, - { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516, upload-time = "2025-08-07T13:53:16.314Z" }, - { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169, upload-time = "2025-08-07T13:18:32.861Z" }, - { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" }, - { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" }, - { url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210, upload-time = "2025-08-07T13:18:24.072Z" }, - { url = "https://files.pythonhosted.org/packages/1c/53/f9c440463b3057485b8594d7a638bed53ba531165ef0ca0e6c364b5cc807/greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b", size = 1564759, upload-time = "2025-11-04T12:42:19.395Z" }, - { url = "https://files.pythonhosted.org/packages/47/e4/3bb4240abdd0a8d23f4f88adec746a3099f0d86bfedb623f063b2e3b4df0/greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929", size = 1634288, upload-time = "2025-11-04T12:42:21.174Z" }, - { url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685, upload-time = "2025-08-07T13:24:38.824Z" }, - { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" }, - { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" }, - { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218, upload-time = "2025-08-07T13:45:30.969Z" }, - { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" }, - { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" }, - { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" }, - { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508, upload-time = "2025-11-04T12:42:23.427Z" }, - { url = "https://files.pythonhosted.org/packages/0d/da/343cd760ab2f92bac1845ca07ee3faea9fe52bee65f7bcb19f16ad7de08b/greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681", size = 1680760, upload-time = "2025-11-04T12:42:25.341Z" }, - { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425, upload-time = "2025-08-07T13:32:27.59Z" }, +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/e5/40dbda2736893e3e53d25838e0f19a2b417dfc122b9989c91918db30b5d3/greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb", size = 190651, upload-time = "2025-12-04T14:49:44.05Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/2f/28592176381b9ab2cafa12829ba7b472d177f3acc35d8fbcf3673d966fff/greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:a1e41a81c7e2825822f4e068c48cb2196002362619e2d70b148f20a831c00739", size = 275140, upload-time = "2025-12-04T14:23:01.282Z" }, + { url = "https://files.pythonhosted.org/packages/2c/80/fbe937bf81e9fca98c981fe499e59a3f45df2a04da0baa5c2be0dca0d329/greenlet-3.3.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f515a47d02da4d30caaa85b69474cec77b7929b2e936ff7fb853d42f4bf8808", size = 599219, upload-time = "2025-12-04T14:50:08.309Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ff/7c985128f0514271b8268476af89aee6866df5eec04ac17dcfbc676213df/greenlet-3.3.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d2d9fd66bfadf230b385fdc90426fcd6eb64db54b40c495b72ac0feb5766c54", size = 610211, upload-time = "2025-12-04T14:57:43.968Z" }, + { url = "https://files.pythonhosted.org/packages/79/07/c47a82d881319ec18a4510bb30463ed6891f2ad2c1901ed5ec23d3de351f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30a6e28487a790417d036088b3bcb3f3ac7d8babaa7d0139edbaddebf3af9492", size = 624311, upload-time = "2025-12-04T15:07:14.697Z" }, + { url = "https://files.pythonhosted.org/packages/fd/8e/424b8c6e78bd9837d14ff7df01a9829fc883ba2ab4ea787d4f848435f23f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:087ea5e004437321508a8d6f20efc4cfec5e3c30118e1417ea96ed1d93950527", size = 612833, upload-time = "2025-12-04T14:26:03.669Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ba/56699ff9b7c76ca12f1cdc27a886d0f81f2189c3455ff9f65246780f713d/greenlet-3.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab97cf74045343f6c60a39913fa59710e4bd26a536ce7ab2397adf8b27e67c39", size = 1567256, upload-time = "2025-12-04T15:04:25.276Z" }, + { url = "https://files.pythonhosted.org/packages/1e/37/f31136132967982d698c71a281a8901daf1a8fbab935dce7c0cf15f942cc/greenlet-3.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5375d2e23184629112ca1ea89a53389dddbffcf417dad40125713d88eb5f96e8", size = 1636483, upload-time = "2025-12-04T14:27:30.804Z" }, + { url = "https://files.pythonhosted.org/packages/7e/71/ba21c3fb8c5dce83b8c01f458a42e99ffdb1963aeec08fff5a18588d8fd7/greenlet-3.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:9ee1942ea19550094033c35d25d20726e4f1c40d59545815e1128ac58d416d38", size = 301833, upload-time = "2025-12-04T14:32:23.929Z" }, + { url = "https://files.pythonhosted.org/packages/d7/7c/f0a6d0ede2c7bf092d00bc83ad5bafb7e6ec9b4aab2fbdfa6f134dc73327/greenlet-3.3.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:60c2ef0f578afb3c8d92ea07ad327f9a062547137afe91f38408f08aacab667f", size = 275671, upload-time = "2025-12-04T14:23:05.267Z" }, + { url = "https://files.pythonhosted.org/packages/44/06/dac639ae1a50f5969d82d2e3dd9767d30d6dbdbab0e1a54010c8fe90263c/greenlet-3.3.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5d554d0712ba1de0a6c94c640f7aeba3f85b3a6e1f2899c11c2c0428da9365", size = 646360, upload-time = "2025-12-04T14:50:10.026Z" }, + { url = "https://files.pythonhosted.org/packages/e0/94/0fb76fe6c5369fba9bf98529ada6f4c3a1adf19e406a47332245ef0eb357/greenlet-3.3.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3a898b1e9c5f7307ebbde4102908e6cbfcb9ea16284a3abe15cab996bee8b9b3", size = 658160, upload-time = "2025-12-04T14:57:45.41Z" }, + { url = "https://files.pythonhosted.org/packages/93/79/d2c70cae6e823fac36c3bbc9077962105052b7ef81db2f01ec3b9bf17e2b/greenlet-3.3.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dcd2bdbd444ff340e8d6bdf54d2f206ccddbb3ccfdcd3c25bf4afaa7b8f0cf45", size = 671388, upload-time = "2025-12-04T15:07:15.789Z" }, + { url = "https://files.pythonhosted.org/packages/b8/14/bab308fc2c1b5228c3224ec2bf928ce2e4d21d8046c161e44a2012b5203e/greenlet-3.3.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5773edda4dc00e173820722711d043799d3adb4f01731f40619e07ea2750b955", size = 660166, upload-time = "2025-12-04T14:26:05.099Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d2/91465d39164eaa0085177f61983d80ffe746c5a1860f009811d498e7259c/greenlet-3.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ac0549373982b36d5fd5d30beb8a7a33ee541ff98d2b502714a09f1169f31b55", size = 1615193, upload-time = "2025-12-04T15:04:27.041Z" }, + { url = "https://files.pythonhosted.org/packages/42/1b/83d110a37044b92423084d52d5d5a3b3a73cafb51b547e6d7366ff62eff1/greenlet-3.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d198d2d977460358c3b3a4dc844f875d1adb33817f0613f663a656f463764ccc", size = 1683653, upload-time = "2025-12-04T14:27:32.366Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/9030e6f9aa8fd7808e9c31ba4c38f87c4f8ec324ee67431d181fe396d705/greenlet-3.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:73f51dd0e0bdb596fb0417e475fa3c5e32d4c83638296e560086b8d7da7c4170", size = 305387, upload-time = "2025-12-04T14:26:51.063Z" }, + { url = "https://files.pythonhosted.org/packages/a0/66/bd6317bc5932accf351fc19f177ffba53712a202f9df10587da8df257c7e/greenlet-3.3.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d6ed6f85fae6cdfdb9ce04c9bf7a08d666cfcfb914e7d006f44f840b46741931", size = 282638, upload-time = "2025-12-04T14:25:20.941Z" }, + { url = "https://files.pythonhosted.org/packages/30/cf/cc81cb030b40e738d6e69502ccbd0dd1bced0588e958f9e757945de24404/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9125050fcf24554e69c4cacb086b87b3b55dc395a8b3ebe6487b045b2614388", size = 651145, upload-time = "2025-12-04T14:50:11.039Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ea/1020037b5ecfe95ca7df8d8549959baceb8186031da83d5ecceff8b08cd2/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:87e63ccfa13c0a0f6234ed0add552af24cc67dd886731f2261e46e241608bee3", size = 654236, upload-time = "2025-12-04T14:57:47.007Z" }, + { url = "https://files.pythonhosted.org/packages/69/cc/1e4bae2e45ca2fa55299f4e85854606a78ecc37fead20d69322f96000504/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2662433acbca297c9153a4023fe2161c8dcfdcc91f10433171cf7e7d94ba2221", size = 662506, upload-time = "2025-12-04T15:07:16.906Z" }, + { url = "https://files.pythonhosted.org/packages/57/b9/f8025d71a6085c441a7eaff0fd928bbb275a6633773667023d19179fe815/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c6e9b9c1527a78520357de498b0e709fb9e2f49c3a513afd5a249007261911b", size = 653783, upload-time = "2025-12-04T14:26:06.225Z" }, + { url = "https://files.pythonhosted.org/packages/f6/c7/876a8c7a7485d5d6b5c6821201d542ef28be645aa024cfe1145b35c120c1/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:286d093f95ec98fdd92fcb955003b8a3d054b4e2cab3e2707a5039e7b50520fd", size = 1614857, upload-time = "2025-12-04T15:04:28.484Z" }, + { url = "https://files.pythonhosted.org/packages/4f/dc/041be1dff9f23dac5f48a43323cd0789cb798342011c19a248d9c9335536/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c10513330af5b8ae16f023e8ddbfb486ab355d04467c4679c5cfe4659975dd9", size = 1676034, upload-time = "2025-12-04T14:27:33.531Z" }, ] [[package]] @@ -1404,29 +1406,29 @@ wheels = [ [[package]] name = "protobuf" -version = "6.33.1" +version = "6.33.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/03/a1440979a3f74f16cab3b75b0da1a1a7f922d56a8ddea96092391998edc0/protobuf-6.33.1.tar.gz", hash = "sha256:97f65757e8d09870de6fd973aeddb92f85435607235d20b2dfed93405d00c85b", size = 443432, upload-time = "2025-11-13T16:44:18.895Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/44/e49ecff446afeec9d1a66d6bbf9adc21e3c7cea7803a920ca3773379d4f6/protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4", size = 444296, upload-time = "2025-12-06T00:17:53.311Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/f1/446a9bbd2c60772ca36556bac8bfde40eceb28d9cc7838755bc41e001d8f/protobuf-6.33.1-cp310-abi3-win32.whl", hash = "sha256:f8d3fdbc966aaab1d05046d0240dd94d40f2a8c62856d41eaa141ff64a79de6b", size = 425593, upload-time = "2025-11-13T16:44:06.275Z" }, - { url = "https://files.pythonhosted.org/packages/a6/79/8780a378c650e3df849b73de8b13cf5412f521ca2ff9b78a45c247029440/protobuf-6.33.1-cp310-abi3-win_amd64.whl", hash = "sha256:923aa6d27a92bf44394f6abf7ea0500f38769d4b07f4be41cb52bd8b1123b9ed", size = 436883, upload-time = "2025-11-13T16:44:09.222Z" }, - { url = "https://files.pythonhosted.org/packages/cd/93/26213ff72b103ae55bb0d73e7fb91ea570ef407c3ab4fd2f1f27cac16044/protobuf-6.33.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:fe34575f2bdde76ac429ec7b570235bf0c788883e70aee90068e9981806f2490", size = 427522, upload-time = "2025-11-13T16:44:10.475Z" }, - { url = "https://files.pythonhosted.org/packages/c2/32/df4a35247923393aa6b887c3b3244a8c941c32a25681775f96e2b418f90e/protobuf-6.33.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:f8adba2e44cde2d7618996b3fc02341f03f5bc3f2748be72dc7b063319276178", size = 324445, upload-time = "2025-11-13T16:44:11.869Z" }, - { url = "https://files.pythonhosted.org/packages/8e/d0/d796e419e2ec93d2f3fa44888861c3f88f722cde02b7c3488fcc6a166820/protobuf-6.33.1-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:0f4cf01222c0d959c2b399142deb526de420be8236f22c71356e2a544e153c53", size = 339161, upload-time = "2025-11-13T16:44:12.778Z" }, - { url = "https://files.pythonhosted.org/packages/1d/2a/3c5f05a4af06649547027d288747f68525755de692a26a7720dced3652c0/protobuf-6.33.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:8fd7d5e0eb08cd5b87fd3df49bc193f5cfd778701f47e11d127d0afc6c39f1d1", size = 323171, upload-time = "2025-11-13T16:44:14.035Z" }, - { url = "https://files.pythonhosted.org/packages/08/b4/46310463b4f6ceef310f8348786f3cff181cea671578e3d9743ba61a459e/protobuf-6.33.1-py3-none-any.whl", hash = "sha256:d595a9fd694fdeb061a62fbe10eb039cc1e444df81ec9bb70c7fc59ebcb1eafa", size = 170477, upload-time = "2025-11-13T16:44:17.633Z" }, + { url = "https://files.pythonhosted.org/packages/bc/91/1e3a34881a88697a7354ffd177e8746e97a722e5e8db101544b47e84afb1/protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d", size = 425603, upload-time = "2025-12-06T00:17:41.114Z" }, + { url = "https://files.pythonhosted.org/packages/64/20/4d50191997e917ae13ad0a235c8b42d8c1ab9c3e6fd455ca16d416944355/protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4", size = 436930, upload-time = "2025-12-06T00:17:43.278Z" }, + { url = "https://files.pythonhosted.org/packages/b2/ca/7e485da88ba45c920fb3f50ae78de29ab925d9e54ef0de678306abfbb497/protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43", size = 427621, upload-time = "2025-12-06T00:17:44.445Z" }, + { url = "https://files.pythonhosted.org/packages/7d/4f/f743761e41d3b2b2566748eb76bbff2b43e14d5fcab694f494a16458b05f/protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e", size = 324460, upload-time = "2025-12-06T00:17:45.678Z" }, + { url = "https://files.pythonhosted.org/packages/b1/fa/26468d00a92824020f6f2090d827078c09c9c587e34cbfd2d0c7911221f8/protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872", size = 339168, upload-time = "2025-12-06T00:17:46.813Z" }, + { url = "https://files.pythonhosted.org/packages/56/13/333b8f421738f149d4fe5e49553bc2a2ab75235486259f689b4b91f96cec/protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f", size = 323270, upload-time = "2025-12-06T00:17:48.253Z" }, + { url = "https://files.pythonhosted.org/packages/0e/15/4f02896cc3df04fc465010a4c6a0cd89810f54617a32a70ef531ed75d61c/protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c", size = 170501, upload-time = "2025-12-06T00:17:52.211Z" }, ] [[package]] name = "psycopg" -version = "3.2.13" +version = "3.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/44/05/d4a05988f15fcf90e0088c735b1f2fc04a30b7fc65461d6ec278f5f2f17a/psycopg-3.2.13.tar.gz", hash = "sha256:309adaeda61d44556046ec9a83a93f42bbe5310120b1995f3af49ab6d9f13c1d", size = 160626, upload-time = "2025-11-21T22:34:32.328Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/1a/7d9ef4fdc13ef7f15b934c393edc97a35c281bb7d3c3329fbfcbe915a7c2/psycopg-3.3.2.tar.gz", hash = "sha256:707a67975ee214d200511177a6a80e56e654754c9afca06a7194ea6bbfde9ca7", size = 165630, upload-time = "2025-12-06T17:34:53.899Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/14/f2724bd1986158a348316e86fdd0837a838b14a711df3f00e47fba597447/psycopg-3.2.13-py3-none-any.whl", hash = "sha256:a481374514f2da627157f767a9336705ebefe93ea7a0522a6cbacba165da179a", size = 206797, upload-time = "2025-11-21T22:29:39.733Z" }, + { url = "https://files.pythonhosted.org/packages/8c/51/2779ccdf9305981a06b21a6b27e8547c948d85c41c76ff434192784a4c93/psycopg-3.3.2-py3-none-any.whl", hash = "sha256:3e94bc5f4690247d734599af56e51bae8e0db8e4311ea413f801fef82b14a99b", size = 212774, upload-time = "2025-12-06T17:31:41.414Z" }, ] [package.optional-dependencies] @@ -1436,27 +1438,31 @@ binary = [ [[package]] name = "psycopg-binary" -version = "3.2.13" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/ec/ef37bb44dc02fcc6c0a3eeb93f4baaac13bcb228633fe38ad3fb5a3f6449/psycopg_binary-3.2.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dbae6ab1966e2b61d97e47220556c330c4608bb4cfb3a124aa0595c39995c068", size = 3995628, upload-time = "2025-11-21T22:31:45.921Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ad/4748f5f1a40248af16dba087dbec50bd335ee025cc1fb9bf64773378ceff/psycopg_binary-3.2.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fae933e4564386199fc54845d85413eedb49760e0bcd2b621fde2dd1825b99b3", size = 4069024, upload-time = "2025-11-21T22:31:50.202Z" }, - { url = "https://files.pythonhosted.org/packages/cf/c2/f02ec6bbc30c7fcd3b39823d2d624b42fae480edeb6e50eb3276281d5635/psycopg_binary-3.2.13-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:13e2f8894d410678529ff9f1211f96c5a93ff142f992b302682b42d924428b61", size = 4615127, upload-time = "2025-11-21T22:31:56.517Z" }, - { url = "https://files.pythonhosted.org/packages/f0/0d/a54fc2cdd672c84175d6869cc823d6ec2a8909318d491f3c24e6077983f2/psycopg_binary-3.2.13-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f26f7009375cf1e92180e5c517c52da1054f7e690dde90e0ed00fa8b5736bcd4", size = 4710267, upload-time = "2025-11-21T22:32:04.585Z" }, - { url = "https://files.pythonhosted.org/packages/9d/b7/067de1acaf3d312253351f3af4121f972584bd36cada6378d4b0cdcebd38/psycopg_binary-3.2.13-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea2fdbcc9142933a47c66970e0df8b363e3bd1ea4c5ce376f2f3d94a9aeec847", size = 4400795, upload-time = "2025-11-21T22:32:08.883Z" }, - { url = "https://files.pythonhosted.org/packages/64/b5/030e6b1ebfc4d3a8fca03adc5fc827982643bad0b01a1268538d17c08ed3/psycopg_binary-3.2.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac92d6bc1d4a41c7459953a9aa727b9966e937e94c9e072527317fd2a67d488b", size = 3851239, upload-time = "2025-11-21T22:32:12.333Z" }, - { url = "https://files.pythonhosted.org/packages/79/6f/0541845364a7de9eae6807060da6a04b22a8eb2e803606d285d9250fbe93/psycopg_binary-3.2.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8b843c00478739e95c46d6d3472b13123b634685f107831a9bfc41503a06ecbd", size = 3525084, upload-time = "2025-11-21T22:32:15.946Z" }, - { url = "https://files.pythonhosted.org/packages/83/ae/6507890dc30a4bbd9d938d4ff3a4079d009a5ad8170af51c7f762438fdbf/psycopg_binary-3.2.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2f63868cc96bc18486cebec24445affbdd7f7debf28fac466ea935a8b5a4753b", size = 3576787, upload-time = "2025-11-21T22:32:19.922Z" }, - { url = "https://files.pythonhosted.org/packages/9d/64/3d1c2f1fd09b60cdfbe68b9a810b357ba505eff6e4bdb1a2d9f6729da64c/psycopg_binary-3.2.13-cp313-cp313-win_amd64.whl", hash = "sha256:594dfbca3326e997ae738d3d339004e8416b1f7390f52ce8dc2d692393e8fa96", size = 2905584, upload-time = "2025-11-21T22:32:23.399Z" }, - { url = "https://files.pythonhosted.org/packages/d3/b4/7656b3d67bedff2b900c8c4671cb6eb5fb99c2fc36da33579cac89779c25/psycopg_binary-3.2.13-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:502a778c3e07c6b3aabfa56ee230e8c264d2debfab42d11535513a01bdfff0d6", size = 3997201, upload-time = "2025-11-21T22:32:28.185Z" }, - { url = "https://files.pythonhosted.org/packages/e0/2e/3b4afbd94d48df19c3931cedba464b109f89d81ac43178e6a3d654b4e8d5/psycopg_binary-3.2.13-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7561a71d764d6f74d66e8b7d844b0f27fa33de508f65c17b1d56a94c73644776", size = 4071631, upload-time = "2025-11-21T22:32:32.594Z" }, - { url = "https://files.pythonhosted.org/packages/5e/8b/107d06d55992e2f13157eb705ba5a47d06c4cf1bed077dff0c567b10c187/psycopg_binary-3.2.13-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9caf14745a1930b4e03fe4072cd7154eaf6e1241d20c42130ed784408a26b24b", size = 4620918, upload-time = "2025-11-21T22:32:37.357Z" }, - { url = "https://files.pythonhosted.org/packages/e1/47/a925620f261b115f31e813a5bfe640f316413b1864094a60162f4a6e4d67/psycopg_binary-3.2.13-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a6cafabdc0bfa37e11c6f365020fd5916b62d6296df581f4dceaa43a2ce680c", size = 4714494, upload-time = "2025-11-21T22:32:42.138Z" }, - { url = "https://files.pythonhosted.org/packages/46/33/bed384665356bb9ba17dd8e104884d87cc2343d16dffdfd9aaa9a159bd4d/psycopg_binary-3.2.13-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96cb5a27e68acac6d74b64fca38592a692de9c4b7827339190698d58027aa45", size = 4403046, upload-time = "2025-11-21T22:32:47.241Z" }, - { url = "https://files.pythonhosted.org/packages/41/88/749d8e8102fb5df502e2ecb053b79e78e3358af01af652b5dbeb96ab7905/psycopg_binary-3.2.13-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:596176ae3dfbf56fc61108870bfe17c7205d33ac28d524909feb5335201daa0a", size = 3859046, upload-time = "2025-11-21T22:32:51.481Z" }, - { url = "https://files.pythonhosted.org/packages/38/7c/f492e63b517d6dcd564e8c43bc15e11a4c712a848adf8938ce33bfd4c867/psycopg_binary-3.2.13-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:cc3a0408435dfbb77eeca5e8050df4b19a6e9b7e5e5583edf524c4a83d6293b2", size = 3531351, upload-time = "2025-11-21T22:32:55.571Z" }, - { url = "https://files.pythonhosted.org/packages/07/5a/d8743eb23944e5cf2a0bbfa92935c140b5beaacdb872be641065ed70ab2c/psycopg_binary-3.2.13-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:65df0d459ffba14082d8ca4bb2f6ffbb2f8d02968f7d34a747e1031934b76b23", size = 3581034, upload-time = "2025-11-21T22:33:01.648Z" }, - { url = "https://files.pythonhosted.org/packages/46/b2/411d4180252144f7eff024894d2d2ebb98c012c944a282fc20250870e461/psycopg_binary-3.2.13-cp314-cp314-win_amd64.whl", hash = "sha256:5c77f156c7316529ed371b5f95a51139e531328ee39c37493a2afcbc1f79d5de", size = 3000162, upload-time = "2025-11-21T22:33:07.378Z" }, +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/73/7ca7cb22b9ac7393fb5de7d28ca97e8347c375c8498b3bff2c99c1f38038/psycopg_binary-3.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fc5a189e89cbfff174588665bb18d28d2d0428366cc9dae5864afcaa2e57380b", size = 4579068, upload-time = "2025-12-06T17:33:39.303Z" }, + { url = "https://files.pythonhosted.org/packages/f5/42/0cf38ff6c62c792fc5b55398a853a77663210ebd51ed6f0c4a05b06f95a6/psycopg_binary-3.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:083c2e182be433f290dc2c516fd72b9b47054fcd305cce791e0a50d9e93e06f2", size = 4657520, upload-time = "2025-12-06T17:33:42.536Z" }, + { url = "https://files.pythonhosted.org/packages/3b/60/df846bc84cbf2231e01b0fff48b09841fe486fa177665e50f4995b1bfa44/psycopg_binary-3.3.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:ac230e3643d1c436a2dfb59ca84357dfc6862c9f372fc5dbd96bafecae581f9f", size = 5452086, upload-time = "2025-12-06T17:33:46.54Z" }, + { url = "https://files.pythonhosted.org/packages/ab/85/30c846a00db86b1b53fd5bfd4b4edfbd0c00de8f2c75dd105610bd7568fc/psycopg_binary-3.3.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d8c899a540f6c7585cee53cddc929dd4d2db90fd828e37f5d4017b63acbc1a5d", size = 5131125, upload-time = "2025-12-06T17:33:50.413Z" }, + { url = "https://files.pythonhosted.org/packages/6d/15/9968732013373f36f8a2a3fb76104dffc8efd9db78709caa5ae1a87b1f80/psycopg_binary-3.3.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50ff10ab8c0abdb5a5451b9315538865b50ba64c907742a1385fdf5f5772b73e", size = 6722914, upload-time = "2025-12-06T17:33:54.544Z" }, + { url = "https://files.pythonhosted.org/packages/b2/ba/29e361fe02143ac5ff5a1ca3e45697344cfbebe2eaf8c4e7eec164bff9a0/psycopg_binary-3.3.2-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:23d2594af848c1fd3d874a9364bef50730124e72df7bb145a20cb45e728c50ed", size = 4966081, upload-time = "2025-12-06T17:33:58.477Z" }, + { url = "https://files.pythonhosted.org/packages/99/45/1be90c8f1a1a237046903e91202fb06708745c179f220b361d6333ed7641/psycopg_binary-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ea4fe6b4ead3bbbe27244ea224fcd1f53cb119afc38b71a2f3ce570149a03e30", size = 4493332, upload-time = "2025-12-06T17:34:02.011Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b5/bbdc07d5f0a5e90c617abd624368182aa131485e18038b2c6c85fc054aed/psycopg_binary-3.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:742ce48cde825b8e52fb1a658253d6d1ff66d152081cbc76aa45e2986534858d", size = 4170781, upload-time = "2025-12-06T17:34:05.298Z" }, + { url = "https://files.pythonhosted.org/packages/d1/2a/0d45e4f4da2bd78c3237ffa03475ef3751f69a81919c54a6e610eb1a7c96/psycopg_binary-3.3.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e22bf6b54df994aff37ab52695d635f1ef73155e781eee1f5fa75bc08b58c8da", size = 3910544, upload-time = "2025-12-06T17:34:08.251Z" }, + { url = "https://files.pythonhosted.org/packages/3a/62/a8e0f092f4dbef9a94b032fb71e214cf0a375010692fbe7493a766339e47/psycopg_binary-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8db9034cde3bcdafc66980f0130813f5c5d19e74b3f2a19fb3cfbc25ad113121", size = 4220070, upload-time = "2025-12-06T17:34:11.392Z" }, + { url = "https://files.pythonhosted.org/packages/09/e6/5fc8d8aff8afa114bb4a94a0341b9309311e8bf3ab32d816032f8b984d4e/psycopg_binary-3.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:df65174c7cf6b05ea273ce955927d3270b3a6e27b0b12762b009ce6082b8d3fc", size = 3540922, upload-time = "2025-12-06T17:34:14.88Z" }, + { url = "https://files.pythonhosted.org/packages/bd/75/ad18c0b97b852aba286d06befb398cc6d383e9dfd0a518369af275a5a526/psycopg_binary-3.3.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9ca24062cd9b2270e4d77576042e9cc2b1d543f09da5aba1f1a3d016cea28390", size = 4596371, upload-time = "2025-12-06T17:34:18.007Z" }, + { url = "https://files.pythonhosted.org/packages/5a/79/91649d94c8d89f84af5da7c9d474bfba35b08eb8f492ca3422b08f0a6427/psycopg_binary-3.3.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c749770da0947bc972e512f35366dd4950c0e34afad89e60b9787a37e97cb443", size = 4675139, upload-time = "2025-12-06T17:34:21.374Z" }, + { url = "https://files.pythonhosted.org/packages/56/ac/b26e004880f054549ec9396594e1ffe435810b0673e428e619ed722e4244/psycopg_binary-3.3.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:03b7cd73fb8c45d272a34ae7249713e32492891492681e3cf11dff9531cf37e9", size = 5456120, upload-time = "2025-12-06T17:34:25.102Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/410681dccd6f2999fb115cc248521ec50dd2b0aba66ae8de7e81efdebbee/psycopg_binary-3.3.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:43b130e3b6edcb5ee856c7167ccb8561b473308c870ed83978ae478613764f1c", size = 5133484, upload-time = "2025-12-06T17:34:28.933Z" }, + { url = "https://files.pythonhosted.org/packages/66/30/ebbab99ea2cfa099d7b11b742ce13415d44f800555bfa4ad2911dc645b71/psycopg_binary-3.3.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c1feba5a8c617922321aef945865334e468337b8fc5c73074f5e63143013b5a", size = 6731818, upload-time = "2025-12-06T17:34:33.094Z" }, + { url = "https://files.pythonhosted.org/packages/70/02/d260646253b7ad805d60e0de47f9b811d6544078452579466a098598b6f4/psycopg_binary-3.3.2-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cabb2a554d9a0a6bf84037d86ca91782f087dfff2a61298d0b00c19c0bc43f6d", size = 4983859, upload-time = "2025-12-06T17:34:36.457Z" }, + { url = "https://files.pythonhosted.org/packages/72/8d/e778d7bad1a7910aa36281f092bd85c5702f508fd9bb0ea2020ffbb6585c/psycopg_binary-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74bc306c4b4df35b09bc8cecf806b271e1c5d708f7900145e4e54a2e5dedfed0", size = 4516388, upload-time = "2025-12-06T17:34:40.129Z" }, + { url = "https://files.pythonhosted.org/packages/bd/f1/64e82098722e2ab3521797584caf515284be09c1e08a872551b6edbb0074/psycopg_binary-3.3.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:d79b0093f0fbf7a962d6a46ae292dc056c65d16a8ee9361f3cfbafd4c197ab14", size = 4192382, upload-time = "2025-12-06T17:34:43.279Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d0/c20f4e668e89494972e551c31be2a0016e3f50d552d7ae9ac07086407599/psycopg_binary-3.3.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:1586e220be05547c77afc326741dd41cc7fba38a81f9931f616ae98865439678", size = 3928660, upload-time = "2025-12-06T17:34:46.757Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e1/99746c171de22539fd5eb1c9ca21dc805b54cfae502d7451d237d1dbc349/psycopg_binary-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:458696a5fa5dad5b6fb5d5862c22454434ce4fe1cf66ca6c0de5f904cbc1ae3e", size = 4239169, upload-time = "2025-12-06T17:34:49.751Z" }, + { url = "https://files.pythonhosted.org/packages/72/f7/212343c1c9cfac35fd943c527af85e9091d633176e2a407a0797856ff7b9/psycopg_binary-3.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:04bb2de4ba69d6f8395b446ede795e8884c040ec71d01dd07ac2b2d18d4153d1", size = 3642122, upload-time = "2025-12-06T17:34:52.506Z" }, ] [[package]] @@ -1508,7 +1514,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.12.4" +version = "2.12.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1516,9 +1522,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" }, + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, ] [package.optional-dependencies] @@ -1674,7 +1680,7 @@ wheels = [ [[package]] name = "pytest" -version = "9.0.1" +version = "9.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1683,9 +1689,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/56/f013048ac4bc4c1d9be45afd4ab209ea62822fb1598f40687e6bf45dcea4/pytest-9.0.1.tar.gz", hash = "sha256:3e9c069ea73583e255c3b21cf46b8d3c56f6e3a1a8f6da94ccb0fcf57b9d73c8", size = 1564125, upload-time = "2025-11-12T13:05:09.333Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl", hash = "sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad", size = 373668, upload-time = "2025-11-12T13:05:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, ] [[package]] @@ -2060,16 +2066,16 @@ wheels = [ [[package]] name = "rich-toolkit" -version = "0.16.0" +version = "0.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/8e/ab512afd71d4e67bb611a57db92a0e967304c97ec61963e99103f5a88069/rich_toolkit-0.16.0.tar.gz", hash = "sha256:2f554b00b194776639f4d80f2706980756b659ceed9f345ebbd9de77d1bdd0f4", size = 183790, upload-time = "2025-11-19T15:26:11.431Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/d0/8f8de36e1abf8339b497ce700dd7251ca465ffca4a1976969b0eaeb596fb/rich_toolkit-0.17.0.tar.gz", hash = "sha256:17ca7a32e613001aa0945ddea27a246f6de01dfc4c12403254c057a8ee542977", size = 187955, upload-time = "2025-11-27T11:10:24.863Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/04/f4bfb5d8a258d395d7fb6fbaa0e3fe7bafae17a2a3e2387e6dea9d6474df/rich_toolkit-0.16.0-py3-none-any.whl", hash = "sha256:3f4307f678c5c1e22c36d89ac05f1cd145ed7174f19c1ce5a4d3664ba77c0f9e", size = 29775, upload-time = "2025-11-19T15:26:10.336Z" }, + { url = "https://files.pythonhosted.org/packages/b2/42/ef2ed40699567661d03b0b511ac46cf6cee736de8f3666819c12d6d20696/rich_toolkit-0.17.0-py3-none-any.whl", hash = "sha256:06fb47a5c5259d6b480287cd38aff5f551b6e1a307f90ed592453dd360e4e71e", size = 31412, upload-time = "2025-11-27T11:10:23.847Z" }, ] [[package]] @@ -2139,53 +2145,53 @@ wheels = [ [[package]] name = "ruff" -version = "0.14.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f0/62b5a1a723fe183650109407fa56abb433b00aa1c0b9ba555f9c4efec2c6/ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc", size = 5669501, upload-time = "2025-11-21T14:26:17.903Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/d2/7dd544116d107fffb24a0064d41a5d2ed1c9d6372d142f9ba108c8e39207/ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3", size = 13326119, upload-time = "2025-11-21T14:25:24.2Z" }, - { url = "https://files.pythonhosted.org/packages/36/6a/ad66d0a3315d6327ed6b01f759d83df3c4d5f86c30462121024361137b6a/ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004", size = 13526007, upload-time = "2025-11-21T14:25:26.906Z" }, - { url = "https://files.pythonhosted.org/packages/a3/9d/dae6db96df28e0a15dea8e986ee393af70fc97fd57669808728080529c37/ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332", size = 12676572, upload-time = "2025-11-21T14:25:29.826Z" }, - { url = "https://files.pythonhosted.org/packages/76/a4/f319e87759949062cfee1b26245048e92e2acce900ad3a909285f9db1859/ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef", size = 13140745, upload-time = "2025-11-21T14:25:32.788Z" }, - { url = "https://files.pythonhosted.org/packages/95/d3/248c1efc71a0a8ed4e8e10b4b2266845d7dfc7a0ab64354afe049eaa1310/ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775", size = 13076486, upload-time = "2025-11-21T14:25:35.601Z" }, - { url = "https://files.pythonhosted.org/packages/a5/19/b68d4563fe50eba4b8c92aa842149bb56dd24d198389c0ed12e7faff4f7d/ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce", size = 13727563, upload-time = "2025-11-21T14:25:38.514Z" }, - { url = "https://files.pythonhosted.org/packages/47/ac/943169436832d4b0e867235abbdb57ce3a82367b47e0280fa7b4eabb7593/ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f", size = 15199755, upload-time = "2025-11-21T14:25:41.516Z" }, - { url = "https://files.pythonhosted.org/packages/c9/b9/288bb2399860a36d4bb0541cb66cce3c0f4156aaff009dc8499be0c24bf2/ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d", size = 14850608, upload-time = "2025-11-21T14:25:44.428Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b1/a0d549dd4364e240f37e7d2907e97ee80587480d98c7799d2d8dc7a2f605/ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440", size = 14118754, upload-time = "2025-11-21T14:25:47.214Z" }, - { url = "https://files.pythonhosted.org/packages/13/ac/9b9fe63716af8bdfddfacd0882bc1586f29985d3b988b3c62ddce2e202c3/ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105", size = 13949214, upload-time = "2025-11-21T14:25:50.002Z" }, - { url = "https://files.pythonhosted.org/packages/12/27/4dad6c6a77fede9560b7df6802b1b697e97e49ceabe1f12baf3ea20862e9/ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821", size = 14106112, upload-time = "2025-11-21T14:25:52.841Z" }, - { url = "https://files.pythonhosted.org/packages/6a/db/23e322d7177873eaedea59a7932ca5084ec5b7e20cb30f341ab594130a71/ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55", size = 13035010, upload-time = "2025-11-21T14:25:55.536Z" }, - { url = "https://files.pythonhosted.org/packages/a8/9c/20e21d4d69dbb35e6a1df7691e02f363423658a20a2afacf2a2c011800dc/ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71", size = 13054082, upload-time = "2025-11-21T14:25:58.625Z" }, - { url = "https://files.pythonhosted.org/packages/66/25/906ee6a0464c3125c8d673c589771a974965c2be1a1e28b5c3b96cb6ef88/ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b", size = 13303354, upload-time = "2025-11-21T14:26:01.816Z" }, - { url = "https://files.pythonhosted.org/packages/4c/58/60577569e198d56922b7ead07b465f559002b7b11d53f40937e95067ca1c/ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185", size = 14054487, upload-time = "2025-11-21T14:26:05.058Z" }, - { url = "https://files.pythonhosted.org/packages/67/0b/8e4e0639e4cc12547f41cb771b0b44ec8225b6b6a93393176d75fe6f7d40/ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85", size = 13013361, upload-time = "2025-11-21T14:26:08.152Z" }, - { url = "https://files.pythonhosted.org/packages/fb/02/82240553b77fd1341f80ebb3eaae43ba011c7a91b4224a9f317d8e6591af/ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9", size = 14432087, upload-time = "2025-11-21T14:26:10.891Z" }, - { url = "https://files.pythonhosted.org/packages/a5/1f/93f9b0fad9470e4c829a5bb678da4012f0c710d09331b860ee555216f4ea/ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2", size = 13520930, upload-time = "2025-11-21T14:26:13.951Z" }, +version = "0.14.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/d9/f7a0c4b3a2bf2556cd5d99b05372c29980249ef71e8e32669ba77428c82c/ruff-0.14.8.tar.gz", hash = "sha256:774ed0dd87d6ce925e3b8496feb3a00ac564bea52b9feb551ecd17e0a23d1eed", size = 5765385, upload-time = "2025-12-04T15:06:17.669Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/b8/9537b52010134b1d2b72870cc3f92d5fb759394094741b09ceccae183fbe/ruff-0.14.8-py3-none-linux_armv6l.whl", hash = "sha256:ec071e9c82eca417f6111fd39f7043acb53cd3fde9b1f95bbed745962e345afb", size = 13441540, upload-time = "2025-12-04T15:06:14.896Z" }, + { url = "https://files.pythonhosted.org/packages/24/00/99031684efb025829713682012b6dd37279b1f695ed1b01725f85fd94b38/ruff-0.14.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8cdb162a7159f4ca36ce980a18c43d8f036966e7f73f866ac8f493b75e0c27e9", size = 13669384, upload-time = "2025-12-04T15:06:51.809Z" }, + { url = "https://files.pythonhosted.org/packages/72/64/3eb5949169fc19c50c04f28ece2c189d3b6edd57e5b533649dae6ca484fe/ruff-0.14.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2e2fcbefe91f9fad0916850edf0854530c15bd1926b6b779de47e9ab619ea38f", size = 12806917, upload-time = "2025-12-04T15:06:08.925Z" }, + { url = "https://files.pythonhosted.org/packages/c4/08/5250babb0b1b11910f470370ec0cbc67470231f7cdc033cee57d4976f941/ruff-0.14.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9d70721066a296f45786ec31916dc287b44040f553da21564de0ab4d45a869b", size = 13256112, upload-time = "2025-12-04T15:06:23.498Z" }, + { url = "https://files.pythonhosted.org/packages/78/4c/6c588e97a8e8c2d4b522c31a579e1df2b4d003eddfbe23d1f262b1a431ff/ruff-0.14.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c87e09b3cd9d126fc67a9ecd3b5b1d3ded2b9c7fce3f16e315346b9d05cfb52", size = 13227559, upload-time = "2025-12-04T15:06:33.432Z" }, + { url = "https://files.pythonhosted.org/packages/23/ce/5f78cea13eda8eceac71b5f6fa6e9223df9b87bb2c1891c166d1f0dce9f1/ruff-0.14.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d62cb310c4fbcb9ee4ac023fe17f984ae1e12b8a4a02e3d21489f9a2a5f730c", size = 13896379, upload-time = "2025-12-04T15:06:02.687Z" }, + { url = "https://files.pythonhosted.org/packages/cf/79/13de4517c4dadce9218a20035b21212a4c180e009507731f0d3b3f5df85a/ruff-0.14.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1af35c2d62633d4da0521178e8a2641c636d2a7153da0bac1b30cfd4ccd91344", size = 15372786, upload-time = "2025-12-04T15:06:29.828Z" }, + { url = "https://files.pythonhosted.org/packages/00/06/33df72b3bb42be8a1c3815fd4fae83fa2945fc725a25d87ba3e42d1cc108/ruff-0.14.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:25add4575ffecc53d60eed3f24b1e934493631b48ebbc6ebaf9d8517924aca4b", size = 14990029, upload-time = "2025-12-04T15:06:36.812Z" }, + { url = "https://files.pythonhosted.org/packages/64/61/0f34927bd90925880394de0e081ce1afab66d7b3525336f5771dcf0cb46c/ruff-0.14.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c943d847b7f02f7db4201a0600ea7d244d8a404fbb639b439e987edcf2baf9a", size = 14407037, upload-time = "2025-12-04T15:06:39.979Z" }, + { url = "https://files.pythonhosted.org/packages/96/bc/058fe0aefc0fbf0d19614cb6d1a3e2c048f7dc77ca64957f33b12cfdc5ef/ruff-0.14.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb6e8bf7b4f627548daa1b69283dac5a296bfe9ce856703b03130732e20ddfe2", size = 14102390, upload-time = "2025-12-04T15:06:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/af/a4/e4f77b02b804546f4c17e8b37a524c27012dd6ff05855d2243b49a7d3cb9/ruff-0.14.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7aaf2974f378e6b01d1e257c6948207aec6a9b5ba53fab23d0182efb887a0e4a", size = 14230793, upload-time = "2025-12-04T15:06:20.497Z" }, + { url = "https://files.pythonhosted.org/packages/3f/52/bb8c02373f79552e8d087cedaffad76b8892033d2876c2498a2582f09dcf/ruff-0.14.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e5758ca513c43ad8a4ef13f0f081f80f08008f410790f3611a21a92421ab045b", size = 13160039, upload-time = "2025-12-04T15:06:49.06Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ad/b69d6962e477842e25c0b11622548df746290cc6d76f9e0f4ed7456c2c31/ruff-0.14.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f74f7ba163b6e85a8d81a590363bf71618847e5078d90827749bfda1d88c9cdf", size = 13205158, upload-time = "2025-12-04T15:06:54.574Z" }, + { url = "https://files.pythonhosted.org/packages/06/63/54f23da1315c0b3dfc1bc03fbc34e10378918a20c0b0f086418734e57e74/ruff-0.14.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eed28f6fafcc9591994c42254f5a5c5ca40e69a30721d2ab18bb0bb3baac3ab6", size = 13469550, upload-time = "2025-12-04T15:05:59.209Z" }, + { url = "https://files.pythonhosted.org/packages/70/7d/a4d7b1961e4903bc37fffb7ddcfaa7beb250f67d97cfd1ee1d5cddb1ec90/ruff-0.14.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:21d48fa744c9d1cb8d71eb0a740c4dd02751a5de9db9a730a8ef75ca34cf138e", size = 14211332, upload-time = "2025-12-04T15:06:06.027Z" }, + { url = "https://files.pythonhosted.org/packages/5d/93/2a5063341fa17054e5c86582136e9895db773e3c2ffb770dde50a09f35f0/ruff-0.14.8-py3-none-win32.whl", hash = "sha256:15f04cb45c051159baebb0f0037f404f1dc2f15a927418f29730f411a79bc4e7", size = 13151890, upload-time = "2025-12-04T15:06:11.668Z" }, + { url = "https://files.pythonhosted.org/packages/02/1c/65c61a0859c0add13a3e1cbb6024b42de587456a43006ca2d4fd3d1618fe/ruff-0.14.8-py3-none-win_amd64.whl", hash = "sha256:9eeb0b24242b5bbff3011409a739929f497f3fb5fe3b5698aba5e77e8c833097", size = 14537826, upload-time = "2025-12-04T15:06:26.409Z" }, + { url = "https://files.pythonhosted.org/packages/6d/63/8b41cea3afd7f58eb64ac9251668ee0073789a3bc9ac6f816c8c6fef986d/ruff-0.14.8-py3-none-win_arm64.whl", hash = "sha256:965a582c93c63fe715fd3e3f8aa37c4b776777203d8e1d8aa3cc0c14424a4b99", size = 13634522, upload-time = "2025-12-04T15:06:43.212Z" }, ] [[package]] name = "s3transfer" -version = "0.15.0" +version = "0.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/bb/940d6af975948c1cc18f44545ffb219d3c35d78ec972b42ae229e8e37e08/s3transfer-0.15.0.tar.gz", hash = "sha256:d36fac8d0e3603eff9b5bfa4282c7ce6feb0301a633566153cbd0b93d11d8379", size = 152185, upload-time = "2025-11-20T20:28:56.327Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/e1/5ef25f52973aa12a19cf4e1375d00932d7fb354ffd310487ba7d44225c1a/s3transfer-0.15.0-py3-none-any.whl", hash = "sha256:6f8bf5caa31a0865c4081186689db1b2534cef721d104eb26101de4b9d6a5852", size = 85984, upload-time = "2025-11-20T20:28:55.046Z" }, + { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" }, ] [[package]] name = "sentry-sdk" -version = "2.46.0" +version = "2.47.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7c/d7/c140a5837649e2bf2ec758494fde1d9a016c76777eab64e75ef38d685bbb/sentry_sdk-2.46.0.tar.gz", hash = "sha256:91821a23460725734b7741523021601593f35731808afc0bb2ba46c27b8acd91", size = 374761, upload-time = "2025-11-24T09:34:13.932Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/2a/d225cbf87b6c8ecce5664db7bcecb82c317e448e3b24a2dcdaacb18ca9a7/sentry_sdk-2.47.0.tar.gz", hash = "sha256:8218891d5e41b4ea8d61d2aed62ed10c80e39d9f2959d6f939efbf056857e050", size = 381895, upload-time = "2025-12-03T14:06:36.846Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/b6/ce7c502a366f4835b1f9c057753f6989a92d3c70cbadb168193f5fb7499b/sentry_sdk-2.46.0-py2.py3-none-any.whl", hash = "sha256:4eeeb60198074dff8d066ea153fa6f241fef1668c10900ea53a4200abc8da9b1", size = 406266, upload-time = "2025-11-24T09:34:12.114Z" }, + { url = "https://files.pythonhosted.org/packages/bd/ac/d6286ea0d49e7b58847faf67b00e56bb4ba3d525281e2ac306e1f1f353da/sentry_sdk-2.47.0-py2.py3-none-any.whl", hash = "sha256:d72f8c61025b7d1d9e52510d03a6247b280094a327dd900d987717a4fce93412", size = 411088, upload-time = "2025-12-03T14:06:35.374Z" }, ] [[package]] @@ -2206,15 +2212,6 @@ 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 = "sniffio" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, -] - [[package]] name = "soupsieve" version = "2.8" @@ -2355,11 +2352,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.5.0" +version = "2.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/43/554c2569b62f49350597348fc3ac70f786e3c32e7f19d266e19817812dd3/urllib3-2.6.0.tar.gz", hash = "sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1", size = 432585, upload-time = "2025-12-05T15:08:47.885Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, + { url = "https://files.pythonhosted.org/packages/56/1a/9ffe814d317c5224166b23e7c47f606d6e473712a2fad0f704ea9b99f246/urllib3-2.6.0-py3-none-any.whl", hash = "sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f", size = 131083, upload-time = "2025-12-05T15:08:45.983Z" }, ] [[package]] From d74517dde9dc8f4085cad29faac14490b6867469 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 8 Dec 2025 11:58:36 +0000 Subject: [PATCH 70/79] fix(backend): Revert to old-school sqlalchemy typing to fix runtime ORM issues --- backend/app/api/auth/models.py | 4 ++-- backend/app/api/background_data/models.py | 2 +- backend/app/api/data_collection/models.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/app/api/auth/models.py b/backend/app/api/auth/models.py index c804717..574c259 100644 --- a/backend/app/api/auth/models.py +++ b/backend/app/api/auth/models.py @@ -68,7 +68,7 @@ class User(SQLModelBaseUserDB, CustomBaseBare, UserBase, TimeStampMixinBare, tab nullable=True, ), ) - organization: Optional[Organization] = Relationship( # noqa: UP045 # Using 'Optional' over Organization | None to avoid issues with sqlmodel type detection + organization: Optional["Organization"] = Relationship( back_populates="members", sa_relationship_kwargs={ "lazy": "selectin", @@ -79,7 +79,7 @@ class User(SQLModelBaseUserDB, CustomBaseBare, UserBase, TimeStampMixinBare, tab organization_role: OrganizationRole | None = Field(default=None, sa_column=Column(SAEnum(OrganizationRole))) # One-to-one relationship with owned Organization - owned_organization: Optional[Organization] = Relationship( # noqa: UP045 # Using 'Optional' over Organization | None to avoid issues with sqlmodel type detection + owned_organization: Optional["Organization"] = Relationship( back_populates="owner", sa_relationship_kwargs={ "uselist": False, diff --git a/backend/app/api/background_data/models.py b/backend/app/api/background_data/models.py index 2ee8f92..1f8bba6 100644 --- a/backend/app/api/background_data/models.py +++ b/backend/app/api/background_data/models.py @@ -89,7 +89,7 @@ class Category(CategoryBase, TimeStampMixinBare, table=True): # Self-referential relationship supercategory_id: int | None = Field(foreign_key="category.id", default=None, nullable=True) - supercategory: Optional[Category] = Relationship( # noqa: UP045 # Using 'Optional' over Category | None to avoid issues with sqlmodel type detection + supercategory: Optional["Category"] = Relationship( back_populates="subcategories", sa_relationship_kwargs={"remote_side": "Category.id", "lazy": "selectin", "join_depth": 1}, ) diff --git a/backend/app/api/data_collection/models.py b/backend/app/api/data_collection/models.py index 1261a22..22b3585 100644 --- a/backend/app/api/data_collection/models.py +++ b/backend/app/api/data_collection/models.py @@ -124,7 +124,7 @@ class Product(ProductBase, TimeStampMixinBare, table=True): # Self-referential relationship for hierarchy parent_id: int | None = Field(default=None, foreign_key="product.id") - parent: Optional[Product] = Relationship( # noqa: UP045 # Using 'Optional' over Product | None to avoid issues with sqlmodel type detection + parent: Optional["Product"] = Relationship( back_populates="components", sa_relationship_kwargs={ "uselist": False, From c6665bd8d7205319bc393205b1c62e1550f48bca Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 8 Dec 2025 16:33:34 +0000 Subject: [PATCH 71/79] feat(backend): Add timeout and use-cookies vars to rclone backup script --- .env.example | 2 ++ backend/scripts/backup/rclone_backup.sh | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 57e0a49..277b256 100644 --- a/.env.example +++ b/.env.example @@ -16,3 +16,5 @@ BACKUP_RSYNC_REMOTE_PATH=/path/to/remote/backup # Remote rclone backup config (for use of backend/scripts/backup/rclone_backup.sh script) BACKUP_RCLONE_REMOTE=myremote:/path/to/remote/backup BACKUP_RCLONE_MULTI_THREAD_STREAMS=16 +BACKUP_RCLONE_TIMEOUT=5m +BACKUP_RCLONE_USE_COOKIES=false diff --git a/backend/scripts/backup/rclone_backup.sh b/backend/scripts/backup/rclone_backup.sh index 16d9db3..b72f2e8 100755 --- a/backend/scripts/backup/rclone_backup.sh +++ b/backend/scripts/backup/rclone_backup.sh @@ -16,8 +16,10 @@ fi # Configuration BACKUP_DIR="${BACKUP_DIR:-$REPO_ROOT/backend/backups}" -BACKUP_RCLONE_REMOTE="${BACKUP_RCLONE_REMOTE?BACKUP_RCLONE_REMOTE not set}" # e.g., "myremote:/backup/relab" +BACKUP_RCLONE_REMOTE="${BACKUP_RCLONE_REMOTE?BACKUP_RCLONE_REMOTE not set}" BACKUP_RCLONE_MULTI_THREAD_STREAMS="${BACKUP_RCLONE_MULTI_THREAD_STREAMS:-16}" +BACKUP_RCLONE_TIMEOUT="${BACKUP_RCLONE_TIMEOUT:-5m}" +BACKUP_RCLONE_USE_COOKIES="${BACKUP_RCLONE_USE_COOKIES:-false}" # Safety Check: If the local dir has 0 files AND the remote has more than 0 files, abort. LOCAL_FILE_COUNT=$(find "$BACKUP_DIR" -type f | wc -l) @@ -37,7 +39,9 @@ rclone sync "$BACKUP_DIR" "$BACKUP_RCLONE_REMOTE" \ --retries 3 \ --low-level-retries 10 \ --stats=30s \ - --stats-one-line-date + --stats-one-line-date \ + --timeout="$BACKUP_RCLONE_TIMEOUT" \ + --use-cookies="$BACKUP_RCLONE_USE_COOKIES" echo "[$(date)] Sync complete. Remote backup stats after sync:" rclone size "$BACKUP_RCLONE_REMOTE" --max-depth=3 2>/dev/null | sed 's/^/ /' From 23750d1bc61f22b8619d260465f61b36c6012dac Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Mon, 9 Feb 2026 13:32:25 +0000 Subject: [PATCH 72/79] fix(backend): Simplify UserRead model for admin level user GET paths --- backend/app/api/auth/routers/admin/users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/api/auth/routers/admin/users.py b/backend/app/api/auth/routers/admin/users.py index 77fc1cf..ac956ac 100644 --- a/backend/app/api/auth/routers/admin/users.py +++ b/backend/app/api/auth/routers/admin/users.py @@ -91,7 +91,7 @@ async def get_users( @router.get( "/{user_id}", summary="View a single user by ID", - response_model=UserReadWithRelationships, + response_model=UserRead, ) async def get_user( user_id: Annotated[UUID4, Path(description="The user's ID")], From bd24f9207e2cb31a63960ed3424860a4d874437a Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 12 Feb 2026 11:02:47 +0000 Subject: [PATCH 73/79] feature(backend): Move from pyright to ty for type checking --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .pre-commit-config.yaml | 25 +- CONTRIBUTING.md | 8 +- backend/app/api/auth/models.py | 6 +- backend/app/api/auth/schemas.py | 8 +- backend/app/api/auth/services/user_manager.py | 2 +- backend/app/api/background_data/crud.py | 8 +- backend/app/api/background_data/models.py | 6 +- backend/app/api/background_data/schemas.py | 12 +- backend/app/api/common/models/base.py | 6 +- backend/app/api/data_collection/crud.py | 18 +- backend/app/api/data_collection/models.py | 4 +- backend/app/api/file_storage/crud.py | 4 +- backend/app/api/file_storage/models/models.py | 4 +- backend/pyproject.toml | 11 +- backend/scripts/backup/rclone_backup.sh | 2 +- backend/scripts/seed/dummy_data.py | 4 +- backend/tests/conftest.py | 4 +- backend/uv.lock | 1156 +++++++++-------- 19 files changed, 673 insertions(+), 617 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 308b556..aea22d4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -19,7 +19,7 @@ Please provide a brief description of the changes in this pull request. ## Checklist - [ ] I've read the [contributing guidelines](../CONTRIBUTING.md) -- [ ] Code follows style guidelines and passes quality checks (ruff, pyright) +- [ ] Code follows style guidelines and passes quality checks (ruff, ty) - [ ] Unit tests added/updated and passing locally - [ ] Documentation updated (if applicable) - [ ] Database migrations created (if applicable) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 898da1b..35e8153 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,9 +40,10 @@ repos: - id: no-commit-to-branch # Prevent commits to main and master branches. - id: trailing-whitespace args: ["--markdown-linebreak-ext", "md"] # Preserve Markdown hard line breaks. + exclude: ^.*/build/.*\.html$ # Exclude generated HTML files because they often have intentional trailing whitespace for formatting. - repo: https://github.com/commitizen-tools/commitizen - rev: v4.10.0 + rev: v4.13.7 hooks: - id: commitizen stages: [commit-msg] @@ -54,15 +55,8 @@ repos: files: ^ (?!(backend/frontend-app|frontend-web)/data/) ### Backend hooks -- repo: https://github.com/RobertCraigie/pyright-python # Lint backend code with Pyright. - rev: v1.1.407 - hooks: - - id: pyright - files: ^backend/(app|scripts|tests)/ - entry: pyright --project backend - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.6 + rev: v0.15.0 hooks: - id: ruff-check # Lint code files: ^backend/(app|scripts|tests)/ @@ -72,14 +66,16 @@ repos: args: ["--config", "backend/pyproject.toml"] - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.13 + rev: 0.10.0 hooks: - id: uv-lock # Update the uv lockfile for the backend. files: ^backend/(uv\.lock|pyproject\.toml|uv\.toml)$ entry: uv lock --project backend - repo: local - hooks: # Check if Alembic migrations are up-to-date. Uses uv to ensure the right environment when executed through VS Code Git extension. + hooks: + # I use uv for local hooks to ensure the right environment when executed through VS Code Git extension. + # Check if Alembic migrations are up-to-date. - id: backend-alembic-autogen-check name: check alembic migrations entry: bash -c 'cd backend && uv run alembic-autogen-check' @@ -87,6 +83,13 @@ repos: files: ^(backend/(app|alembic)/|alembic\.ini$) pass_filenames: false stages: [pre-commit] + # Run Ty for static type checking. + - id: ty + name: type check with Ty + files: ^backend/(app|scripts|tests)/ + entry: bash -c 'cd backend && uv run ty check' + language: system + types: [python] ### Frontend hooks - repo: local diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8e89656..164be12 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -284,14 +284,14 @@ We use several tools to ensure code quality: 1. [Ruff](https://docs.astral.sh/ruff/) for linting and code style enforcement (see [`pyproject.toml`](backend/pyproject.toml) for rules): ```bash - uv run ruff check . - uv run ruff format . + uv run ruff check + uv run ruff format ``` -1. [Pyright](https://github.com/microsoft/pyright) for static type checking: +1. [Ty](https://docs.astral.sh/ty/) for static type checking: ```bash - uv run pyright + uv run ty check ``` #### Backend Testing diff --git a/backend/app/api/auth/models.py b/backend/app/api/auth/models.py index 574c259..f412e12 100644 --- a/backend/app/api/auth/models.py +++ b/backend/app/api/auth/models.py @@ -33,7 +33,7 @@ class UserBase(BaseModel): username: str | None = Field(index=True, unique=True, default=None, min_length=2, max_length=50) - model_config = ConfigDict(use_enum_values=True) # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config = ConfigDict(use_enum_values=True) class User(SQLModelBaseUserDB, CustomBaseBare, UserBase, TimeStampMixinBare, table=True): @@ -68,7 +68,7 @@ class User(SQLModelBaseUserDB, CustomBaseBare, UserBase, TimeStampMixinBare, tab nullable=True, ), ) - organization: Optional["Organization"] = Relationship( + organization: Organization | None = Relationship( back_populates="members", sa_relationship_kwargs={ "lazy": "selectin", @@ -79,7 +79,7 @@ class User(SQLModelBaseUserDB, CustomBaseBare, UserBase, TimeStampMixinBare, tab organization_role: OrganizationRole | None = Field(default=None, sa_column=Column(SAEnum(OrganizationRole))) # One-to-one relationship with owned Organization - owned_organization: Optional["Organization"] = Relationship( + owned_organization: Organization | None = Relationship( back_populates="owner", sa_relationship_kwargs={ "uselist": False, diff --git a/backend/app/api/auth/schemas.py b/backend/app/api/auth/schemas.py index 8db9611..f2ee013 100644 --- a/backend/app/api/auth/schemas.py +++ b/backend/app/api/auth/schemas.py @@ -73,7 +73,7 @@ class UserCreate(UserCreateBase): organization_id: UUID4 | None = None - model_config: ConfigDict = ConfigDict( # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict( { "json_schema_extra": { "examples": [ @@ -94,7 +94,7 @@ class UserCreateWithOrganization(UserCreateBase): organization: OrganizationCreate - model_config: ConfigDict = ConfigDict( # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict( { "json_schema_extra": { "examples": [ @@ -119,7 +119,7 @@ class UserReadPublic(UserBase): class UserRead(UserBase, schemas.BaseUser[uuid.UUID]): """Read schema for users.""" - model_config: ConfigDict = ConfigDict( # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict( { "json_schema_extra": { "examples": [ @@ -160,7 +160,7 @@ class UserUpdate(UserBase, schemas.BaseUserUpdate): # Override password field to include password format in JSON schema password: str | None = Field(default=None, json_schema_extra={"format": "password"}, min_length=8) - model_config: ConfigDict = ConfigDict( # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict( { "json_schema_extra": { "examples": [ diff --git a/backend/app/api/auth/services/user_manager.py b/backend/app/api/auth/services/user_manager.py index 40eab49..1a7aa6f 100644 --- a/backend/app/api/auth/services/user_manager.py +++ b/backend/app/api/auth/services/user_manager.py @@ -103,7 +103,7 @@ async def update( return await super().update(user_update, user, safe, request) - async def validate_password( # pyright: ignore [reportIncompatibleMethodOverride] # Allow overriding user type in method + async def validate_password( self, password: str | SecretStr, user: UserCreate | User, diff --git a/backend/app/api/background_data/crud.py b/backend/app/api/background_data/crud.py index 999b3c8..cab79c5 100644 --- a/backend/app/api/background_data/crud.py +++ b/backend/app/api/background_data/crud.py @@ -361,7 +361,7 @@ async def create_material(db: AsyncSession, material: MaterialCreate | MaterialC # Create links await create_model_links( db, - id1=db_material.id, # pyright: ignore[reportArgumentType] # material ID is guaranteed by database flush above, + id1=db_material.id, # ty: ignore[invalid-argument-type] # material ID is guaranteed by database flush above id1_field="material_id", id2_set=material.category_ids, id2_field="category_id", @@ -420,7 +420,7 @@ async def add_categories_to_material( await create_model_links( db, - id1=db_material.id, # pyright: ignore[reportArgumentType] # material ID is guaranteed by database flush above, + id1=db_material.id, # ty: ignore[invalid-argument-type] # material ID is guaranteed by database flush above id1_field="material_id", id2_set=category_ids, id2_field="category_id", @@ -503,7 +503,7 @@ async def create_product_type( if isinstance(product_type, ProductTypeCreateWithCategories) and product_type.category_ids: await create_model_links( db, - id1=db_product_type.id, # pyright: ignore[reportArgumentType] # material ID is guaranteed by database flush above, + id1=db_product_type.id, # ty: ignore[invalid-argument-type] # material ID is guaranteed by database flush above id1_field="product_type", id2_set=product_type.category_ids, id2_field="category_id", @@ -561,7 +561,7 @@ async def add_categories_to_product_type( await create_model_links( db, - id1=db_product_type.id, # pyright: ignore[reportArgumentType] # material ID is guaranteed by database flush above, + id1=db_product_type.id, # ty: ignore[invalid-argument-type] # material ID is guaranteed by database flush above id1_field="product_type", id2_set=category_ids, id2_field="category_id", diff --git a/backend/app/api/background_data/models.py b/backend/app/api/background_data/models.py index 1f8bba6..b9dcc79 100644 --- a/backend/app/api/background_data/models.py +++ b/backend/app/api/background_data/models.py @@ -56,7 +56,7 @@ class TaxonomyBase(CustomBase): default=None, max_length=500, description="Source of the taxonomy data, e.g. URL, IRI or citation key" ) - model_config: ConfigDict = ConfigDict(use_enum_values=True) # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict(use_enum_values=True) class Taxonomy(TaxonomyBase, TimeStampMixinBare, table=True): @@ -66,7 +66,7 @@ class Taxonomy(TaxonomyBase, TimeStampMixinBare, table=True): categories: list[Category] = Relationship(back_populates="taxonomy", cascade_delete=True) - model_config: ConfigDict = ConfigDict(use_enum_values=True, arbitrary_types_allowed=True) # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict(use_enum_values=True, arbitrary_types_allowed=True) # Magic methods def __str__(self) -> str: @@ -89,7 +89,7 @@ class Category(CategoryBase, TimeStampMixinBare, table=True): # Self-referential relationship supercategory_id: int | None = Field(foreign_key="category.id", default=None, nullable=True) - supercategory: Optional["Category"] = Relationship( + supercategory: Category | None = Relationship( back_populates="subcategories", sa_relationship_kwargs={"remote_side": "Category.id", "lazy": "selectin", "join_depth": 1}, ) diff --git a/backend/app/api/background_data/schemas.py b/backend/app/api/background_data/schemas.py index 682a6c2..b594013 100644 --- a/backend/app/api/background_data/schemas.py +++ b/backend/app/api/background_data/schemas.py @@ -59,7 +59,7 @@ class CategoryCreateWithSubCategories(CategoryCreateWithinTaxonomyWithSubCategor class CategoryReadAsSubCategory(BaseReadSchema, CategoryBase): """Schema for reading subcategory information.""" - model_config: ConfigDict = ConfigDict( # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict( json_schema_extra={ "examples": [ { @@ -78,7 +78,7 @@ class CategoryRead(CategoryReadAsSubCategory): taxonomy_id: PositiveInt = Field(description="ID of the taxonomy") supercategory_id: PositiveInt | None = None - model_config: ConfigDict = ConfigDict( # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see + model_config: ConfigDict = ConfigDict( json_schema_extra={ "examples": [ { @@ -115,7 +115,7 @@ class CategoryReadAsSubCategoryWithRecursiveSubCategories(CategoryReadAsSubCateg default_factory=list, description="List of subcategories" ) - model_config: ConfigDict = ConfigDict( # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict( json_schema_extra={ "examples": [ { @@ -168,7 +168,7 @@ class CategoryUpdate(BaseUpdateSchema): name: str | None = Field(default=None, min_length=2, max_length=100, description="Name of the category") description: str | None = Field(default=None, max_length=500, description="Description of the category") - model_config: ConfigDict = ConfigDict( # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict( { "json_schema_extra": { "examples": [ @@ -200,7 +200,7 @@ class TaxonomyCreateWithCategories(BaseCreateSchema, TaxonomyBase): class TaxonomyRead(BaseReadSchema, TaxonomyBase): """Schema for reading minimal taxonomy information.""" - model_config: ConfigDict = ConfigDict( # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict( { "json_schema_extra": { "examples": [ @@ -223,7 +223,7 @@ class TaxonomyReadWithCategoryTree(TaxonomyRead): default_factory=set, description="Set of categories in the taxonomy" ) - model_config: ConfigDict = ConfigDict( # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict( { "json_schema_extra": { "examples": [ diff --git a/backend/app/api/common/models/base.py b/backend/app/api/common/models/base.py index 54e3b41..6ebc160 100644 --- a/backend/app/api/common/models/base.py +++ b/backend/app/api/common/models/base.py @@ -138,12 +138,12 @@ class TimeStampMixinBare: created_at: datetime | None = Field( default=None, - sa_type=TIMESTAMP(timezone=True), # pyright: ignore [reportArgumentType] # SQLModel mixins with SQLAlchemy Column specifications are complicated, see https://github.com/fastapi/sqlmodel/discussions/743 + sa_type=TIMESTAMP(timezone=True), sa_column_kwargs={"server_default": func.now()}, ) updated_at: datetime | None = Field( default=None, - sa_type=TIMESTAMP(timezone=True), # pyright: ignore [reportArgumentType] + sa_type=TIMESTAMP(timezone=True), sa_column_kwargs={"server_default": func.now(), "onupdate": func.now()}, ) @@ -160,7 +160,7 @@ class SingleParentMixin[ParentTypeEnum](SQLModel): parent_type: ParentTypeEnum # Type of the parent object. To be overridden by derived classes. - model_config: ConfigDict = ConfigDict(arbitrary_types_allowed=True) # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict(arbitrary_types_allowed=True) @classmethod def get_parent_type_description(cls, enum_class: type[Enum]) -> str: diff --git a/backend/app/api/data_collection/crud.py b/backend/app/api/data_collection/crud.py index cc9e20e..2532afb 100644 --- a/backend/app/api/data_collection/crud.py +++ b/backend/app/api/data_collection/crud.py @@ -254,7 +254,7 @@ async def create_component( db_component = Product( **component_data, parent_id=parent_product_id, - owner_id=owner_id, # pyright: ignore[reportArgumentType] # owner ID is guaranteed by database fetch above + owner_id=owner_id, ) db.add(db_component) await db.flush() # Assign component ID @@ -263,14 +263,14 @@ async def create_component( if component.physical_properties: db_physical_property = PhysicalProperties( **component.physical_properties.model_dump(), - product_id=db_component.id, # pyright: ignore[reportArgumentType] # component ID is guaranteed by database flush above + product_id=db_component.id, ) db.add(db_physical_property) if component.circularity_properties: db_circularity_property = CircularityProperties( **component.circularity_properties.model_dump(), - product_id=db_component.id, # pyright: ignore[reportArgumentType] # component ID is guaranteed by database flush above + product_id=db_component.id, ) db.add(db_circularity_property) @@ -291,7 +291,7 @@ async def create_component( # Create material-product links db.add_all( - MaterialProductLink(**material.model_dump(), product_id=db_component.id) # pyright: ignore[reportArgumentType] # product ID is guaranteed by database flush above + MaterialProductLink(**material.model_dump(), product_id=db_component.id) for material in component.bill_of_materials ) @@ -301,7 +301,7 @@ async def create_component( await create_component( db, subcomponent, - parent_product_id=db_component.id, # pyright: ignore[reportArgumentType] # component ID is guaranteed by database flush above + parent_product_id=db_component.id, # ty: ignore[invalid-argument-type] # component ID is guaranteed by database flush above owner_id=owner_id, _is_recursive_call=True, ) @@ -347,14 +347,14 @@ async def create_product( if product.physical_properties: db_physical_properties = PhysicalProperties( **product.physical_properties.model_dump(), - product_id=db_product.id, # pyright: ignore[reportArgumentType] # product ID is guaranteed by database flush above + product_id=db_product.id, ) db.add(db_physical_properties) if product.circularity_properties: db_circularity_properties = CircularityProperties( **product.circularity_properties.model_dump(), - product_id=db_product.id, # pyright: ignore[reportArgumentType] # product ID is guaranteed by database flush above + product_id=db_product.id, ) db.add(db_circularity_properties) @@ -375,7 +375,7 @@ async def create_product( # Create material-product links db.add_all( - MaterialProductLink(**material.model_dump(), product_id=db_product.id) # pyright: ignore[reportArgumentType] # product ID is guaranteed by database flush above + MaterialProductLink(**material.model_dump(), product_id=db_product.id) for material in product.bill_of_materials ) @@ -386,7 +386,7 @@ async def create_product( await create_component( db, component, - parent_product_id=db_product.id, # pyright: ignore[reportArgumentType] # component ID is guaranteed by database flush above + parent_product_id=db_product.id, # ty: ignore[invalid-argument-type] # component ID is guaranteed by database flush above owner_id=owner_id, _is_recursive_call=True, ) diff --git a/backend/app/api/data_collection/models.py b/backend/app/api/data_collection/models.py index 22b3585..6106d77 100644 --- a/backend/app/api/data_collection/models.py +++ b/backend/app/api/data_collection/models.py @@ -124,7 +124,7 @@ class Product(ProductBase, TimeStampMixinBare, table=True): # Self-referential relationship for hierarchy parent_id: int | None = Field(default=None, foreign_key="product.id") - parent: Optional["Product"] = Relationship( + parent: Product | None = Relationship( back_populates="components", sa_relationship_kwargs={ "uselist": False, @@ -299,7 +299,7 @@ async def traverse(product: Product, quantity_multiplier: float) -> None: await traverse(self, 1.0) return total_materials - model_config: ConfigDict = ConfigDict(arbitrary_types_allowed=True) # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict(arbitrary_types_allowed=True) def __str__(self): return f"{self.name} (id: {self.id})" diff --git a/backend/app/api/file_storage/crud.py b/backend/app/api/file_storage/crud.py index 775cd82..2c90086 100644 --- a/backend/app/api/file_storage/crud.py +++ b/backend/app/api/file_storage/crud.py @@ -108,7 +108,7 @@ async def create_file(db: AsyncSession, file_data: FileCreate) -> File: id=file_id, description=file_data.description, filename=original_filename, - file=file_data.file, # pyright: ignore [reportArgumentType] # Incoming UploadFile cannot be preemptively cast to FileType because of how FastAPI-storages works. + file=file_data.file, parent_type=file_data.parent_type, ) @@ -192,7 +192,7 @@ async def create_image(db: AsyncSession, image_data: ImageCreateFromForm | Image description=image_data.description, image_metadata=image_data.image_metadata, filename=original_filename, - file=image_data.file, # pyright: ignore [reportArgumentType] # Incoming UploadFile cannot be preemptively cast to FileType because of how FastAPI-storages works. + file=image_data.file, parent_type=image_data.parent_type, ) diff --git a/backend/app/api/file_storage/models/models.py b/backend/app/api/file_storage/models/models.py index d159d99..fe7622d 100644 --- a/backend/app/api/file_storage/models/models.py +++ b/backend/app/api/file_storage/models/models.py @@ -74,7 +74,7 @@ class File(FileBase, TimeStampMixinBare, SingleParentMixin[FileParentType], tabl product_type: ProductType = Relationship(back_populates="files") # Model configuration - model_config: ConfigDict = ConfigDict(arbitrary_types_allowed=True, use_enum_values=True) # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict(arbitrary_types_allowed=True, use_enum_values=True) @cached_property def file_url(self) -> str: @@ -138,7 +138,7 @@ class Image(ImageBase, TimeStampMixinBare, SingleParentMixin, table=True): product_type: ProductType = Relationship(back_populates="images") # Model configuration - model_config: ConfigDict = ConfigDict(arbitrary_types_allowed=True) # pyright: ignore [reportIncompatibleVariableOverride] # This is not a type override, see https://github.com/fastapi/sqlmodel/discussions/855 + model_config: ConfigDict = ConfigDict(arbitrary_types_allowed=True) @cached_property def image_url(self) -> str: diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 24aa274..10482c0 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -66,8 +66,8 @@ dev = [ # Development dependencies. See also https://docs.astral.sh/uv/concepts/dependencies/#development-dependencies "alembic-autogen-check >=1.1.1", "paracelsus>=0.9.0", - "pyright>=1.1.402", "ruff >=0.12.1", + "ty>=0.0.15", ] api = [ @@ -79,7 +79,7 @@ "google-auth>=2.40.3", "itsdangerous>=2.2.0", "markupsafe >=3.0.2", -] + ] migrations = ["alembic >=1.16.2", "alembic-postgresql-enum >=1.7.0", "openpyxl>=3.1.5", "pandas>=2.3.3"] @@ -104,13 +104,6 @@ "app.api.plugins.rpi_cam.models", ] -[tool.pyright] - # NOTE: Pyright doesn't work well by only setting exclude, so we explicitly include the directories we want to check - include = ["app", "scripts", "tests"] - typeCheckingMode = "standard" - venv = ".venv" - venvPath = "." - [tool.pytest.ini_options] asyncio_mode = "auto" diff --git a/backend/scripts/backup/rclone_backup.sh b/backend/scripts/backup/rclone_backup.sh index b72f2e8..2620d12 100755 --- a/backend/scripts/backup/rclone_backup.sh +++ b/backend/scripts/backup/rclone_backup.sh @@ -41,7 +41,7 @@ rclone sync "$BACKUP_DIR" "$BACKUP_RCLONE_REMOTE" \ --stats=30s \ --stats-one-line-date \ --timeout="$BACKUP_RCLONE_TIMEOUT" \ - --use-cookies="$BACKUP_RCLONE_USE_COOKIES" + --use-cookies="$BACKUP_RCLONE_USE_COOKIES" echo "[$(date)] Sync complete. Remote backup stats after sync:" rclone size "$BACKUP_RCLONE_REMOTE" --max-depth=3 2>/dev/null | sed 's/^/ /' diff --git a/backend/scripts/seed/dummy_data.py b/backend/scripts/seed/dummy_data.py index e9d4a6e..a6c2792 100755 --- a/backend/scripts/seed/dummy_data.py +++ b/backend/scripts/seed/dummy_data.py @@ -295,14 +295,14 @@ async def seed_products( brand=data["brand"], model=data["model"], product_type_id=product_type.id, - owner_id=next(iter(user_map.values())).id, # pyright: ignore [reportArgumentType] # ID is guaranteed because these objects have been committed to the DB earlier. + owner_id=next(iter(user_map.values())).id, ) session.add(product) await session.commit() await session.refresh(product) # Ensures ID for product # Now create physical properties with product_id - physical_props = PhysicalProperties(**data["physical_properties"], product_id=product.id) # pyright: ignore [reportArgumentType] # ID is guaranteed because these objects have been committed to the DB earlier. + physical_props = PhysicalProperties(**data["physical_properties"], product_id=product.id) # ty: ignore[invalid-argument-type] # properties ID is guaranteed by database flush above session.add(physical_props) await session.commit() diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 5deafde..889d359 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -102,13 +102,13 @@ async def override_get_db() -> AsyncGenerator[AsyncSession]: ### Email fixtures @pytest.fixture -def email_context() -> dict: +def email_context() -> EmailContextFactory: """Return a realistic email template context dict using FactoryBoy/Faker.""" return EmailContextFactory() @pytest.fixture -def email_data() -> dict: +def email_data() -> EmailDataFactory: """Return realistic test data for email functions using FactoryBoy/Faker.""" return EmailDataFactory() diff --git a/backend/uv.lock b/backend/uv.lock index b05fc7c..ad8a0cd 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -2,8 +2,12 @@ version = 1 revision = 3 requires-python = ">=3.13" resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version < '3.14'", + "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]] @@ -17,16 +21,16 @@ wheels = [ [[package]] name = "alembic" -version = "1.17.2" +version = "1.18.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mako" }, { name = "sqlalchemy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/a6/74c8cadc2882977d80ad756a13857857dbcf9bd405bc80b662eb10651282/alembic-1.17.2.tar.gz", hash = "sha256:bbe9751705c5e0f14877f02d46c53d10885e377e3d90eda810a016f9baa19e8e", size = 1988064, upload-time = "2025-11-14T20:35:04.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/13/8b084e0f2efb0275a1d534838844926f798bd766566b1375174e2448cd31/alembic-1.18.4.tar.gz", hash = "sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc", size = 2056725, upload-time = "2026-02-10T16:00:47.195Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/88/6237e97e3385b57b5f1528647addea5cc03d4d65d5979ab24327d41fb00d/alembic-1.17.2-py3-none-any.whl", hash = "sha256:f483dd1fe93f6c5d49217055e4d15b905b425b6af906746abb35b69c1996c4e6", size = 248554, upload-time = "2025-11-14T20:35:05.699Z" }, + { url = "https://files.pythonhosted.org/packages/d2/29/6533c317b74f707ea28f8d633734dbda2119bbadfc61b2f3640ba835d0f7/alembic-1.18.4-py3-none-any.whl", hash = "sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a", size = 263893, upload-time = "2026-02-10T16:00:49.997Z" }, ] [[package]] @@ -44,15 +48,15 @@ wheels = [ [[package]] name = "alembic-postgresql-enum" -version = "1.8.0" +version = "1.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "alembic" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/04/e465cb5c051fb056b7fadda7667b3e1fb4d32d7f19533e3bbff071c73788/alembic_postgresql_enum-1.8.0.tar.gz", hash = "sha256:132cd5fdc4a2a0b6498f3d89ea1c7b2a5ddc3281ddd84edae7259ec4c0a215a0", size = 15858, upload-time = "2025-07-20T12:25:50.626Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/69/1c9b6dbcb99d2eb1b59807fb6e14717d9686bfc567b2d2740cb1d1be055f/alembic_postgresql_enum-1.9.0.tar.gz", hash = "sha256:5ce76d0fca97e7e11b56ca416aa367aaaf308ff90f3ed5d38a6f160357bebecd", size = 18444, upload-time = "2026-02-04T18:25:55.477Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/80/4e6e841f9a0403b520b8f28650c2cdf5905e25bd4ff403b43daec580fed3/alembic_postgresql_enum-1.8.0-py3-none-any.whl", hash = "sha256:0e62833f8d1aca2c58fa09cae1d4a52472fb32d2dde32b68c84515fffcf401d5", size = 23697, upload-time = "2025-07-20T12:25:49.048Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d5/3a15fadb3468082a93c1752ad45a47a739d4fc482472d03d9ad73dc11937/alembic_postgresql_enum-1.9.0-py3-none-any.whl", hash = "sha256:308666d6f1b154ec4dd10b8599e606711bc7eff050f7fce2e77e435734da5d76", size = 27674, upload-time = "2026-02-04T18:25:53.915Z" }, ] [[package]] @@ -75,26 +79,26 @@ wheels = [ [[package]] name = "anyio" -version = "4.12.0" +version = "4.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/16/ce/8a777047513153587e5434fd752e89334ac33e379aa3497db860eeb60377/anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0", size = 228266, upload-time = "2025-11-28T23:37:38.911Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/9c/36c5c37947ebfb8c7f22e0eb6e4d188ee2d53aa3880f3f2744fb894f0cb1/anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb", size = 113362, upload-time = "2025-11-28T23:36:57.897Z" }, + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, ] [[package]] name = "argon2-cffi" -version = "23.1.0" +version = "25.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argon2-cffi-bindings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/31/fa/57ec2c6d16ecd2ba0cf15f3c7d1c3c2e7b5fcb83555ff56d7ab10888ec8f/argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08", size = 42798, upload-time = "2023-08-15T14:13:12.711Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/6a/e8a041599e78b6b3752da48000b14c8d1e8a04ded09c88c714ba047f34f5/argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea", size = 15124, upload-time = "2023-08-15T14:13:10.752Z" }, + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, ] [[package]] @@ -174,52 +178,68 @@ wheels = [ [[package]] name = "bcrypt" -version = "4.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/5d/6d7433e0f3cd46ce0b43cd65e1db465ea024dbb8216fb2404e919c2ad77b/bcrypt-4.3.0.tar.gz", hash = "sha256:3a3fd2204178b6d2adcf09cb4f6426ffef54762577a7c9b54c159008cb288c18", size = 25697, upload-time = "2025-02-28T01:24:09.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/2c/3d44e853d1fe969d229bd58d39ae6902b3d924af0e2b5a60d17d4b809ded/bcrypt-4.3.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281", size = 483719, upload-time = "2025-02-28T01:22:34.539Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e2/58ff6e2a22eca2e2cff5370ae56dba29d70b1ea6fc08ee9115c3ae367795/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb", size = 272001, upload-time = "2025-02-28T01:22:38.078Z" }, - { url = "https://files.pythonhosted.org/packages/37/1f/c55ed8dbe994b1d088309e366749633c9eb90d139af3c0a50c102ba68a1a/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59e1aa0e2cd871b08ca146ed08445038f42ff75968c7ae50d2fdd7860ade2180", size = 277451, upload-time = "2025-02-28T01:22:40.787Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1c/794feb2ecf22fe73dcfb697ea7057f632061faceb7dcf0f155f3443b4d79/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:0042b2e342e9ae3d2ed22727c1262f76cc4f345683b5c1715f0250cf4277294f", size = 272792, upload-time = "2025-02-28T01:22:43.144Z" }, - { url = "https://files.pythonhosted.org/packages/13/b7/0b289506a3f3598c2ae2bdfa0ea66969812ed200264e3f61df77753eee6d/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74a8d21a09f5e025a9a23e7c0fd2c7fe8e7503e4d356c0a2c1486ba010619f09", size = 289752, upload-time = "2025-02-28T01:22:45.56Z" }, - { url = "https://files.pythonhosted.org/packages/dc/24/d0fb023788afe9e83cc118895a9f6c57e1044e7e1672f045e46733421fe6/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:0142b2cb84a009f8452c8c5a33ace5e3dfec4159e7735f5afe9a4d50a8ea722d", size = 277762, upload-time = "2025-02-28T01:22:47.023Z" }, - { url = "https://files.pythonhosted.org/packages/e4/38/cde58089492e55ac4ef6c49fea7027600c84fd23f7520c62118c03b4625e/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:12fa6ce40cde3f0b899729dbd7d5e8811cb892d31b6f7d0334a1f37748b789fd", size = 272384, upload-time = "2025-02-28T01:22:49.221Z" }, - { url = "https://files.pythonhosted.org/packages/de/6a/d5026520843490cfc8135d03012a413e4532a400e471e6188b01b2de853f/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:5bd3cca1f2aa5dbcf39e2aa13dd094ea181f48959e1071265de49cc2b82525af", size = 277329, upload-time = "2025-02-28T01:22:51.603Z" }, - { url = "https://files.pythonhosted.org/packages/b3/a3/4fc5255e60486466c389e28c12579d2829b28a527360e9430b4041df4cf9/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:335a420cfd63fc5bc27308e929bee231c15c85cc4c496610ffb17923abf7f231", size = 305241, upload-time = "2025-02-28T01:22:53.283Z" }, - { url = "https://files.pythonhosted.org/packages/c7/15/2b37bc07d6ce27cc94e5b10fd5058900eb8fb11642300e932c8c82e25c4a/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:0e30e5e67aed0187a1764911af023043b4542e70a7461ad20e837e94d23e1d6c", size = 309617, upload-time = "2025-02-28T01:22:55.461Z" }, - { url = "https://files.pythonhosted.org/packages/5f/1f/99f65edb09e6c935232ba0430c8c13bb98cb3194b6d636e61d93fe60ac59/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b8d62290ebefd49ee0b3ce7500f5dbdcf13b81402c05f6dafab9a1e1b27212f", size = 335751, upload-time = "2025-02-28T01:22:57.81Z" }, - { url = "https://files.pythonhosted.org/packages/00/1b/b324030c706711c99769988fcb694b3cb23f247ad39a7823a78e361bdbb8/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2ef6630e0ec01376f59a006dc72918b1bf436c3b571b80fa1968d775fa02fe7d", size = 355965, upload-time = "2025-02-28T01:22:59.181Z" }, - { url = "https://files.pythonhosted.org/packages/aa/dd/20372a0579dd915dfc3b1cd4943b3bca431866fcb1dfdfd7518c3caddea6/bcrypt-4.3.0-cp313-cp313t-win32.whl", hash = "sha256:7a4be4cbf241afee43f1c3969b9103a41b40bcb3a3f467ab19f891d9bc4642e4", size = 155316, upload-time = "2025-02-28T01:23:00.763Z" }, - { url = "https://files.pythonhosted.org/packages/6d/52/45d969fcff6b5577c2bf17098dc36269b4c02197d551371c023130c0f890/bcrypt-4.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c1949bf259a388863ced887c7861da1df681cb2388645766c89fdfd9004c669", size = 147752, upload-time = "2025-02-28T01:23:02.908Z" }, - { url = "https://files.pythonhosted.org/packages/11/22/5ada0b9af72b60cbc4c9a399fdde4af0feaa609d27eb0adc61607997a3fa/bcrypt-4.3.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:f81b0ed2639568bf14749112298f9e4e2b28853dab50a8b357e31798686a036d", size = 498019, upload-time = "2025-02-28T01:23:05.838Z" }, - { url = "https://files.pythonhosted.org/packages/b8/8c/252a1edc598dc1ce57905be173328eda073083826955ee3c97c7ff5ba584/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:864f8f19adbe13b7de11ba15d85d4a428c7e2f344bac110f667676a0ff84924b", size = 279174, upload-time = "2025-02-28T01:23:07.274Z" }, - { url = "https://files.pythonhosted.org/packages/29/5b/4547d5c49b85f0337c13929f2ccbe08b7283069eea3550a457914fc078aa/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e36506d001e93bffe59754397572f21bb5dc7c83f54454c990c74a468cd589e", size = 283870, upload-time = "2025-02-28T01:23:09.151Z" }, - { url = "https://files.pythonhosted.org/packages/be/21/7dbaf3fa1745cb63f776bb046e481fbababd7d344c5324eab47f5ca92dd2/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:842d08d75d9fe9fb94b18b071090220697f9f184d4547179b60734846461ed59", size = 279601, upload-time = "2025-02-28T01:23:11.461Z" }, - { url = "https://files.pythonhosted.org/packages/6d/64/e042fc8262e971347d9230d9abbe70d68b0a549acd8611c83cebd3eaec67/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7c03296b85cb87db865d91da79bf63d5609284fc0cab9472fdd8367bbd830753", size = 297660, upload-time = "2025-02-28T01:23:12.989Z" }, - { url = "https://files.pythonhosted.org/packages/50/b8/6294eb84a3fef3b67c69b4470fcdd5326676806bf2519cda79331ab3c3a9/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:62f26585e8b219cdc909b6a0069efc5e4267e25d4a3770a364ac58024f62a761", size = 284083, upload-time = "2025-02-28T01:23:14.5Z" }, - { url = "https://files.pythonhosted.org/packages/62/e6/baff635a4f2c42e8788fe1b1633911c38551ecca9a749d1052d296329da6/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:beeefe437218a65322fbd0069eb437e7c98137e08f22c4660ac2dc795c31f8bb", size = 279237, upload-time = "2025-02-28T01:23:16.686Z" }, - { url = "https://files.pythonhosted.org/packages/39/48/46f623f1b0c7dc2e5de0b8af5e6f5ac4cc26408ac33f3d424e5ad8da4a90/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:97eea7408db3a5bcce4a55d13245ab3fa566e23b4c67cd227062bb49e26c585d", size = 283737, upload-time = "2025-02-28T01:23:18.897Z" }, - { url = "https://files.pythonhosted.org/packages/49/8b/70671c3ce9c0fca4a6cc3cc6ccbaa7e948875a2e62cbd146e04a4011899c/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:191354ebfe305e84f344c5964c7cd5f924a3bfc5d405c75ad07f232b6dffb49f", size = 312741, upload-time = "2025-02-28T01:23:21.041Z" }, - { url = "https://files.pythonhosted.org/packages/27/fb/910d3a1caa2d249b6040a5caf9f9866c52114d51523ac2fb47578a27faee/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:41261d64150858eeb5ff43c753c4b216991e0ae16614a308a15d909503617732", size = 316472, upload-time = "2025-02-28T01:23:23.183Z" }, - { url = "https://files.pythonhosted.org/packages/dc/cf/7cf3a05b66ce466cfb575dbbda39718d45a609daa78500f57fa9f36fa3c0/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:33752b1ba962ee793fa2b6321404bf20011fe45b9afd2a842139de3011898fef", size = 343606, upload-time = "2025-02-28T01:23:25.361Z" }, - { url = "https://files.pythonhosted.org/packages/e3/b8/e970ecc6d7e355c0d892b7f733480f4aa8509f99b33e71550242cf0b7e63/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:50e6e80a4bfd23a25f5c05b90167c19030cf9f87930f7cb2eacb99f45d1c3304", size = 362867, upload-time = "2025-02-28T01:23:26.875Z" }, - { url = "https://files.pythonhosted.org/packages/a9/97/8d3118efd8354c555a3422d544163f40d9f236be5b96c714086463f11699/bcrypt-4.3.0-cp38-abi3-win32.whl", hash = "sha256:67a561c4d9fb9465ec866177e7aebcad08fe23aaf6fbd692a6fab69088abfc51", size = 160589, upload-time = "2025-02-28T01:23:28.381Z" }, - { url = "https://files.pythonhosted.org/packages/29/07/416f0b99f7f3997c69815365babbc2e8754181a4b1899d921b3c7d5b6f12/bcrypt-4.3.0-cp38-abi3-win_amd64.whl", hash = "sha256:584027857bc2843772114717a7490a37f68da563b3620f78a849bcb54dc11e62", size = 152794, upload-time = "2025-02-28T01:23:30.187Z" }, - { url = "https://files.pythonhosted.org/packages/6e/c1/3fa0e9e4e0bfd3fd77eb8b52ec198fd6e1fd7e9402052e43f23483f956dd/bcrypt-4.3.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d3efb1157edebfd9128e4e46e2ac1a64e0c1fe46fb023158a407c7892b0f8c3", size = 498969, upload-time = "2025-02-28T01:23:31.945Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d4/755ce19b6743394787fbd7dff6bf271b27ee9b5912a97242e3caf125885b/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08bacc884fd302b611226c01014eca277d48f0a05187666bca23aac0dad6fe24", size = 279158, upload-time = "2025-02-28T01:23:34.161Z" }, - { url = "https://files.pythonhosted.org/packages/9b/5d/805ef1a749c965c46b28285dfb5cd272a7ed9fa971f970435a5133250182/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6746e6fec103fcd509b96bacdfdaa2fbde9a553245dbada284435173a6f1aef", size = 284285, upload-time = "2025-02-28T01:23:35.765Z" }, - { url = "https://files.pythonhosted.org/packages/ab/2b/698580547a4a4988e415721b71eb45e80c879f0fb04a62da131f45987b96/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:afe327968aaf13fc143a56a3360cb27d4ad0345e34da12c7290f1b00b8fe9a8b", size = 279583, upload-time = "2025-02-28T01:23:38.021Z" }, - { url = "https://files.pythonhosted.org/packages/f2/87/62e1e426418204db520f955ffd06f1efd389feca893dad7095bf35612eec/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d9af79d322e735b1fc33404b5765108ae0ff232d4b54666d46730f8ac1a43676", size = 297896, upload-time = "2025-02-28T01:23:39.575Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c6/8fedca4c2ada1b6e889c52d2943b2f968d3427e5d65f595620ec4c06fa2f/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f1e3ffa1365e8702dc48c8b360fef8d7afeca482809c5e45e653af82ccd088c1", size = 284492, upload-time = "2025-02-28T01:23:40.901Z" }, - { url = "https://files.pythonhosted.org/packages/4d/4d/c43332dcaaddb7710a8ff5269fcccba97ed3c85987ddaa808db084267b9a/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3004df1b323d10021fda07a813fd33e0fd57bef0e9a480bb143877f6cba996fe", size = 279213, upload-time = "2025-02-28T01:23:42.653Z" }, - { url = "https://files.pythonhosted.org/packages/dc/7f/1e36379e169a7df3a14a1c160a49b7b918600a6008de43ff20d479e6f4b5/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:531457e5c839d8caea9b589a1bcfe3756b0547d7814e9ce3d437f17da75c32b0", size = 284162, upload-time = "2025-02-28T01:23:43.964Z" }, - { url = "https://files.pythonhosted.org/packages/1c/0a/644b2731194b0d7646f3210dc4d80c7fee3ecb3a1f791a6e0ae6bb8684e3/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:17a854d9a7a476a89dcef6c8bd119ad23e0f82557afbd2c442777a16408e614f", size = 312856, upload-time = "2025-02-28T01:23:46.011Z" }, - { url = "https://files.pythonhosted.org/packages/dc/62/2a871837c0bb6ab0c9a88bf54de0fc021a6a08832d4ea313ed92a669d437/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6fb1fd3ab08c0cbc6826a2e0447610c6f09e983a281b919ed721ad32236b8b23", size = 316726, upload-time = "2025-02-28T01:23:47.575Z" }, - { url = "https://files.pythonhosted.org/packages/0c/a1/9898ea3faac0b156d457fd73a3cb9c2855c6fd063e44b8522925cdd8ce46/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e965a9c1e9a393b8005031ff52583cedc15b7884fce7deb8b0346388837d6cfe", size = 343664, upload-time = "2025-02-28T01:23:49.059Z" }, - { url = "https://files.pythonhosted.org/packages/40/f2/71b4ed65ce38982ecdda0ff20c3ad1b15e71949c78b2c053df53629ce940/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:79e70b8342a33b52b55d93b3a59223a844962bef479f6a0ea318ebbcadf71505", size = 363128, upload-time = "2025-02-28T01:23:50.399Z" }, - { url = "https://files.pythonhosted.org/packages/11/99/12f6a58eca6dea4be992d6c681b7ec9410a1d9f5cf368c61437e31daa879/bcrypt-4.3.0-cp39-abi3-win32.whl", hash = "sha256:b4d4e57f0a63fd0b358eb765063ff661328f69a04494427265950c71b992a39a", size = 160598, upload-time = "2025-02-28T01:23:51.775Z" }, - { url = "https://files.pythonhosted.org/packages/a9/cf/45fb5261ece3e6b9817d3d82b2f343a505fd58674a92577923bc500bd1aa/bcrypt-4.3.0-cp39-abi3-win_amd64.whl", hash = "sha256:e53e074b120f2877a35cc6c736b8eb161377caae8925c17688bd46ba56daaa5b", size = 152799, upload-time = "2025-02-28T01:23:53.139Z" }, +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/36/3329e2518d70ad8e2e5817d5a4cac6bba05a47767ec416c7d020a965f408/bcrypt-5.0.0.tar.gz", hash = "sha256:f748f7c2d6fd375cc93d3fba7ef4a9e3a092421b8dbf34d8d4dc06be9492dfdd", size = 25386, upload-time = "2025-09-25T19:50:47.829Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/85/3e65e01985fddf25b64ca67275bb5bdb4040bd1a53b66d355c6c37c8a680/bcrypt-5.0.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f3c08197f3039bec79cee59a606d62b96b16669cff3949f21e74796b6e3cd2be", size = 481806, upload-time = "2025-09-25T19:49:05.102Z" }, + { url = "https://files.pythonhosted.org/packages/44/dc/01eb79f12b177017a726cbf78330eb0eb442fae0e7b3dfd84ea2849552f3/bcrypt-5.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:200af71bc25f22006f4069060c88ed36f8aa4ff7f53e67ff04d2ab3f1e79a5b2", size = 268626, upload-time = "2025-09-25T19:49:06.723Z" }, + { url = "https://files.pythonhosted.org/packages/8c/cf/e82388ad5959c40d6afd94fb4743cc077129d45b952d46bdc3180310e2df/bcrypt-5.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:baade0a5657654c2984468efb7d6c110db87ea63ef5a4b54732e7e337253e44f", size = 271853, upload-time = "2025-09-25T19:49:08.028Z" }, + { url = "https://files.pythonhosted.org/packages/ec/86/7134b9dae7cf0efa85671651341f6afa695857fae172615e960fb6a466fa/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c58b56cdfb03202b3bcc9fd8daee8e8e9b6d7e3163aa97c631dfcfcc24d36c86", size = 269793, upload-time = "2025-09-25T19:49:09.727Z" }, + { url = "https://files.pythonhosted.org/packages/cc/82/6296688ac1b9e503d034e7d0614d56e80c5d1a08402ff856a4549cb59207/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4bfd2a34de661f34d0bda43c3e4e79df586e4716ef401fe31ea39d69d581ef23", size = 289930, upload-time = "2025-09-25T19:49:11.204Z" }, + { url = "https://files.pythonhosted.org/packages/d1/18/884a44aa47f2a3b88dd09bc05a1e40b57878ecd111d17e5bba6f09f8bb77/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ed2e1365e31fc73f1825fa830f1c8f8917ca1b3ca6185773b349c20fd606cec2", size = 272194, upload-time = "2025-09-25T19:49:12.524Z" }, + { url = "https://files.pythonhosted.org/packages/0e/8f/371a3ab33c6982070b674f1788e05b656cfbf5685894acbfef0c65483a59/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:83e787d7a84dbbfba6f250dd7a5efd689e935f03dd83b0f919d39349e1f23f83", size = 269381, upload-time = "2025-09-25T19:49:14.308Z" }, + { url = "https://files.pythonhosted.org/packages/b1/34/7e4e6abb7a8778db6422e88b1f06eb07c47682313997ee8a8f9352e5a6f1/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:137c5156524328a24b9fac1cb5db0ba618bc97d11970b39184c1d87dc4bf1746", size = 271750, upload-time = "2025-09-25T19:49:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/54f416be2499bd72123c70d98d36c6cd61a4e33d9b89562c22481c81bb30/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:38cac74101777a6a7d3b3e3cfefa57089b5ada650dce2baf0cbdd9d65db22a9e", size = 303757, upload-time = "2025-09-25T19:49:17.244Z" }, + { url = "https://files.pythonhosted.org/packages/13/62/062c24c7bcf9d2826a1a843d0d605c65a755bc98002923d01fd61270705a/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:d8d65b564ec849643d9f7ea05c6d9f0cd7ca23bdd4ac0c2dbef1104ab504543d", size = 306740, upload-time = "2025-09-25T19:49:18.693Z" }, + { url = "https://files.pythonhosted.org/packages/d5/c8/1fdbfc8c0f20875b6b4020f3c7dc447b8de60aa0be5faaf009d24242aec9/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:741449132f64b3524e95cd30e5cd3343006ce146088f074f31ab26b94e6c75ba", size = 334197, upload-time = "2025-09-25T19:49:20.523Z" }, + { url = "https://files.pythonhosted.org/packages/a6/c1/8b84545382d75bef226fbc6588af0f7b7d095f7cd6a670b42a86243183cd/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:212139484ab3207b1f0c00633d3be92fef3c5f0af17cad155679d03ff2ee1e41", size = 352974, upload-time = "2025-09-25T19:49:22.254Z" }, + { url = "https://files.pythonhosted.org/packages/10/a6/ffb49d4254ed085e62e3e5dd05982b4393e32fe1e49bb1130186617c29cd/bcrypt-5.0.0-cp313-cp313t-win32.whl", hash = "sha256:9d52ed507c2488eddd6a95bccee4e808d3234fa78dd370e24bac65a21212b861", size = 148498, upload-time = "2025-09-25T19:49:24.134Z" }, + { url = "https://files.pythonhosted.org/packages/48/a9/259559edc85258b6d5fc5471a62a3299a6aa37a6611a169756bf4689323c/bcrypt-5.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f6984a24db30548fd39a44360532898c33528b74aedf81c26cf29c51ee47057e", size = 145853, upload-time = "2025-09-25T19:49:25.702Z" }, + { url = "https://files.pythonhosted.org/packages/2d/df/9714173403c7e8b245acf8e4be8876aac64a209d1b392af457c79e60492e/bcrypt-5.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9fffdb387abe6aa775af36ef16f55e318dcda4194ddbf82007a6f21da29de8f5", size = 139626, upload-time = "2025-09-25T19:49:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/f8/14/c18006f91816606a4abe294ccc5d1e6f0e42304df5a33710e9e8e95416e1/bcrypt-5.0.0-cp314-cp314t-macosx_10_12_universal2.whl", hash = "sha256:4870a52610537037adb382444fefd3706d96d663ac44cbb2f37e3919dca3d7ef", size = 481862, upload-time = "2025-09-25T19:49:28.365Z" }, + { url = "https://files.pythonhosted.org/packages/67/49/dd074d831f00e589537e07a0725cf0e220d1f0d5d8e85ad5bbff251c45aa/bcrypt-5.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48f753100931605686f74e27a7b49238122aa761a9aefe9373265b8b7aa43ea4", size = 268544, upload-time = "2025-09-25T19:49:30.39Z" }, + { url = "https://files.pythonhosted.org/packages/f5/91/50ccba088b8c474545b034a1424d05195d9fcbaaf802ab8bfe2be5a4e0d7/bcrypt-5.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f70aadb7a809305226daedf75d90379c397b094755a710d7014b8b117df1ebbf", size = 271787, upload-time = "2025-09-25T19:49:32.144Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e7/d7dba133e02abcda3b52087a7eea8c0d4f64d3e593b4fffc10c31b7061f3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:744d3c6b164caa658adcb72cb8cc9ad9b4b75c7db507ab4bc2480474a51989da", size = 269753, upload-time = "2025-09-25T19:49:33.885Z" }, + { url = "https://files.pythonhosted.org/packages/33/fc/5b145673c4b8d01018307b5c2c1fc87a6f5a436f0ad56607aee389de8ee3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a28bc05039bdf3289d757f49d616ab3efe8cf40d8e8001ccdd621cd4f98f4fc9", size = 289587, upload-time = "2025-09-25T19:49:35.144Z" }, + { url = "https://files.pythonhosted.org/packages/27/d7/1ff22703ec6d4f90e62f1a5654b8867ef96bafb8e8102c2288333e1a6ca6/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7f277a4b3390ab4bebe597800a90da0edae882c6196d3038a73adf446c4f969f", size = 272178, upload-time = "2025-09-25T19:49:36.793Z" }, + { url = "https://files.pythonhosted.org/packages/c8/88/815b6d558a1e4d40ece04a2f84865b0fef233513bd85fd0e40c294272d62/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:79cfa161eda8d2ddf29acad370356b47f02387153b11d46042e93a0a95127493", size = 269295, upload-time = "2025-09-25T19:49:38.164Z" }, + { url = "https://files.pythonhosted.org/packages/51/8c/e0db387c79ab4931fc89827d37608c31cc57b6edc08ccd2386139028dc0d/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a5393eae5722bcef046a990b84dff02b954904c36a194f6cfc817d7dca6c6f0b", size = 271700, upload-time = "2025-09-25T19:49:39.917Z" }, + { url = "https://files.pythonhosted.org/packages/06/83/1570edddd150f572dbe9fc00f6203a89fc7d4226821f67328a85c330f239/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f4c94dec1b5ab5d522750cb059bb9409ea8872d4494fd152b53cca99f1ddd8c", size = 334034, upload-time = "2025-09-25T19:49:41.227Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f2/ea64e51a65e56ae7a8a4ec236c2bfbdd4b23008abd50ac33fbb2d1d15424/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0cae4cb350934dfd74c020525eeae0a5f79257e8a201c0c176f4b84fdbf2a4b4", size = 352766, upload-time = "2025-09-25T19:49:43.08Z" }, + { url = "https://files.pythonhosted.org/packages/d7/d4/1a388d21ee66876f27d1a1f41287897d0c0f1712ef97d395d708ba93004c/bcrypt-5.0.0-cp314-cp314t-win32.whl", hash = "sha256:b17366316c654e1ad0306a6858e189fc835eca39f7eb2cafd6aaca8ce0c40a2e", size = 152449, upload-time = "2025-09-25T19:49:44.971Z" }, + { url = "https://files.pythonhosted.org/packages/3f/61/3291c2243ae0229e5bca5d19f4032cecad5dfb05a2557169d3a69dc0ba91/bcrypt-5.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:92864f54fb48b4c718fc92a32825d0e42265a627f956bc0361fe869f1adc3e7d", size = 149310, upload-time = "2025-09-25T19:49:46.162Z" }, + { url = "https://files.pythonhosted.org/packages/3e/89/4b01c52ae0c1a681d4021e5dd3e45b111a8fb47254a274fa9a378d8d834b/bcrypt-5.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dd19cf5184a90c873009244586396a6a884d591a5323f0e8a5922560718d4993", size = 143761, upload-time = "2025-09-25T19:49:47.345Z" }, + { url = "https://files.pythonhosted.org/packages/84/29/6237f151fbfe295fe3e074ecc6d44228faa1e842a81f6d34a02937ee1736/bcrypt-5.0.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:fc746432b951e92b58317af8e0ca746efe93e66555f1b40888865ef5bf56446b", size = 494553, upload-time = "2025-09-25T19:49:49.006Z" }, + { url = "https://files.pythonhosted.org/packages/45/b6/4c1205dde5e464ea3bd88e8742e19f899c16fa8916fb8510a851fae985b5/bcrypt-5.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c2388ca94ffee269b6038d48747f4ce8df0ffbea43f31abfa18ac72f0218effb", size = 275009, upload-time = "2025-09-25T19:49:50.581Z" }, + { url = "https://files.pythonhosted.org/packages/3b/71/427945e6ead72ccffe77894b2655b695ccf14ae1866cd977e185d606dd2f/bcrypt-5.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:560ddb6ec730386e7b3b26b8b4c88197aaed924430e7b74666a586ac997249ef", size = 278029, upload-time = "2025-09-25T19:49:52.533Z" }, + { url = "https://files.pythonhosted.org/packages/17/72/c344825e3b83c5389a369c8a8e58ffe1480b8a699f46c127c34580c4666b/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d79e5c65dcc9af213594d6f7f1fa2c98ad3fc10431e7aa53c176b441943efbdd", size = 275907, upload-time = "2025-09-25T19:49:54.709Z" }, + { url = "https://files.pythonhosted.org/packages/0b/7e/d4e47d2df1641a36d1212e5c0514f5291e1a956a7749f1e595c07a972038/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2b732e7d388fa22d48920baa267ba5d97cca38070b69c0e2d37087b381c681fd", size = 296500, upload-time = "2025-09-25T19:49:56.013Z" }, + { url = "https://files.pythonhosted.org/packages/0f/c3/0ae57a68be2039287ec28bc463b82e4b8dc23f9d12c0be331f4782e19108/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0c8e093ea2532601a6f686edbc2c6b2ec24131ff5c52f7610dd64fa4553b5464", size = 278412, upload-time = "2025-09-25T19:49:57.356Z" }, + { url = "https://files.pythonhosted.org/packages/45/2b/77424511adb11e6a99e3a00dcc7745034bee89036ad7d7e255a7e47be7d8/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5b1589f4839a0899c146e8892efe320c0fa096568abd9b95593efac50a87cb75", size = 275486, upload-time = "2025-09-25T19:49:59.116Z" }, + { url = "https://files.pythonhosted.org/packages/43/0a/405c753f6158e0f3f14b00b462d8bca31296f7ecfc8fc8bc7919c0c7d73a/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:89042e61b5e808b67daf24a434d89bab164d4de1746b37a8d173b6b14f3db9ff", size = 277940, upload-time = "2025-09-25T19:50:00.869Z" }, + { url = "https://files.pythonhosted.org/packages/62/83/b3efc285d4aadc1fa83db385ec64dcfa1707e890eb42f03b127d66ac1b7b/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e3cf5b2560c7b5a142286f69bde914494b6d8f901aaa71e453078388a50881c4", size = 310776, upload-time = "2025-09-25T19:50:02.393Z" }, + { url = "https://files.pythonhosted.org/packages/95/7d/47ee337dacecde6d234890fe929936cb03ebc4c3a7460854bbd9c97780b8/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f632fd56fc4e61564f78b46a2269153122db34988e78b6be8b32d28507b7eaeb", size = 312922, upload-time = "2025-09-25T19:50:04.232Z" }, + { url = "https://files.pythonhosted.org/packages/d6/3a/43d494dfb728f55f4e1cf8fd435d50c16a2d75493225b54c8d06122523c6/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:801cad5ccb6b87d1b430f183269b94c24f248dddbbc5c1f78b6ed231743e001c", size = 341367, upload-time = "2025-09-25T19:50:05.559Z" }, + { url = "https://files.pythonhosted.org/packages/55/ab/a0727a4547e383e2e22a630e0f908113db37904f58719dc48d4622139b5c/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3cf67a804fc66fc217e6914a5635000259fbbbb12e78a99488e4d5ba445a71eb", size = 359187, upload-time = "2025-09-25T19:50:06.916Z" }, + { url = "https://files.pythonhosted.org/packages/1b/bb/461f352fdca663524b4643d8b09e8435b4990f17fbf4fea6bc2a90aa0cc7/bcrypt-5.0.0-cp38-abi3-win32.whl", hash = "sha256:3abeb543874b2c0524ff40c57a4e14e5d3a66ff33fb423529c88f180fd756538", size = 153752, upload-time = "2025-09-25T19:50:08.515Z" }, + { url = "https://files.pythonhosted.org/packages/41/aa/4190e60921927b7056820291f56fc57d00d04757c8b316b2d3c0d1d6da2c/bcrypt-5.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:35a77ec55b541e5e583eb3436ffbbf53b0ffa1fa16ca6782279daf95d146dcd9", size = 150881, upload-time = "2025-09-25T19:50:09.742Z" }, + { url = "https://files.pythonhosted.org/packages/54/12/cd77221719d0b39ac0b55dbd39358db1cd1246e0282e104366ebbfb8266a/bcrypt-5.0.0-cp38-abi3-win_arm64.whl", hash = "sha256:cde08734f12c6a4e28dc6755cd11d3bdfea608d93d958fffbe95a7026ebe4980", size = 144931, upload-time = "2025-09-25T19:50:11.016Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ba/2af136406e1c3839aea9ecadc2f6be2bcd1eff255bd451dd39bcf302c47a/bcrypt-5.0.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0c418ca99fd47e9c59a301744d63328f17798b5947b0f791e9af3c1c499c2d0a", size = 495313, upload-time = "2025-09-25T19:50:12.309Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ee/2f4985dbad090ace5ad1f7dd8ff94477fe089b5fab2040bd784a3d5f187b/bcrypt-5.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddb4e1500f6efdd402218ffe34d040a1196c072e07929b9820f363a1fd1f4191", size = 275290, upload-time = "2025-09-25T19:50:13.673Z" }, + { url = "https://files.pythonhosted.org/packages/e4/6e/b77ade812672d15cf50842e167eead80ac3514f3beacac8902915417f8b7/bcrypt-5.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7aeef54b60ceddb6f30ee3db090351ecf0d40ec6e2abf41430997407a46d2254", size = 278253, upload-time = "2025-09-25T19:50:15.089Z" }, + { url = "https://files.pythonhosted.org/packages/36/c4/ed00ed32f1040f7990dac7115f82273e3c03da1e1a1587a778d8cea496d8/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f0ce778135f60799d89c9693b9b398819d15f1921ba15fe719acb3178215a7db", size = 276084, upload-time = "2025-09-25T19:50:16.699Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/fa6e16145e145e87f1fa351bbd54b429354fd72145cd3d4e0c5157cf4c70/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a71f70ee269671460b37a449f5ff26982a6f2ba493b3eabdd687b4bf35f875ac", size = 297185, upload-time = "2025-09-25T19:50:18.525Z" }, + { url = "https://files.pythonhosted.org/packages/24/b4/11f8a31d8b67cca3371e046db49baa7c0594d71eb40ac8121e2fc0888db0/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8429e1c410b4073944f03bd778a9e066e7fad723564a52ff91841d278dfc822", size = 278656, upload-time = "2025-09-25T19:50:19.809Z" }, + { url = "https://files.pythonhosted.org/packages/ac/31/79f11865f8078e192847d2cb526e3fa27c200933c982c5b2869720fa5fce/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:edfcdcedd0d0f05850c52ba3127b1fce70b9f89e0fe5ff16517df7e81fa3cbb8", size = 275662, upload-time = "2025-09-25T19:50:21.567Z" }, + { url = "https://files.pythonhosted.org/packages/d4/8d/5e43d9584b3b3591a6f9b68f755a4da879a59712981ef5ad2a0ac1379f7a/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:611f0a17aa4a25a69362dcc299fda5c8a3d4f160e2abb3831041feb77393a14a", size = 278240, upload-time = "2025-09-25T19:50:23.305Z" }, + { url = "https://files.pythonhosted.org/packages/89/48/44590e3fc158620f680a978aafe8f87a4c4320da81ed11552f0323aa9a57/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:db99dca3b1fdc3db87d7c57eac0c82281242d1eabf19dcb8a6b10eb29a2e72d1", size = 311152, upload-time = "2025-09-25T19:50:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/e4fbfc46f14f47b0d20493669a625da5827d07e8a88ee460af6cd9768b44/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:5feebf85a9cefda32966d8171f5db7e3ba964b77fdfe31919622256f80f9cf42", size = 313284, upload-time = "2025-09-25T19:50:26.268Z" }, + { url = "https://files.pythonhosted.org/packages/25/ae/479f81d3f4594456a01ea2f05b132a519eff9ab5768a70430fa1132384b1/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3ca8a166b1140436e058298a34d88032ab62f15aae1c598580333dc21d27ef10", size = 341643, upload-time = "2025-09-25T19:50:28.02Z" }, + { url = "https://files.pythonhosted.org/packages/df/d2/36a086dee1473b14276cd6ea7f61aef3b2648710b5d7f1c9e032c29b859f/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:61afc381250c3182d9078551e3ac3a41da14154fbff647ddf52a769f588c4172", size = 359698, upload-time = "2025-09-25T19:50:31.347Z" }, + { url = "https://files.pythonhosted.org/packages/c0/f6/688d2cd64bfd0b14d805ddb8a565e11ca1fb0fd6817175d58b10052b6d88/bcrypt-5.0.0-cp39-abi3-win32.whl", hash = "sha256:64d7ce196203e468c457c37ec22390f1a61c85c6f0b8160fd752940ccfb3a683", size = 153725, upload-time = "2025-09-25T19:50:34.384Z" }, + { url = "https://files.pythonhosted.org/packages/9f/b9/9d9a641194a730bda138b3dfe53f584d61c58cd5230e37566e83ec2ffa0d/bcrypt-5.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:64ee8434b0da054d830fa8e89e1c8bf30061d539044a39524ff7dec90481e5c2", size = 150912, upload-time = "2025-09-25T19:50:35.69Z" }, + { url = "https://files.pythonhosted.org/packages/27/44/d2ef5e87509158ad2187f4dd0852df80695bb1ee0cfe0a684727b01a69e0/bcrypt-5.0.0-cp39-abi3-win_arm64.whl", hash = "sha256:f2347d3534e76bf50bca5500989d6c1d05ed64b440408057a37673282c654927", size = 144953, upload-time = "2025-09-25T19:50:37.32Z" }, ] [[package]] @@ -246,30 +266,30 @@ wheels = [ [[package]] name = "boto3" -version = "1.42.4" +version = "1.42.47" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f3/31/246916eec4fc5ff7bebf7e75caf47ee4d72b37d4120b6943e3460956e618/boto3-1.42.4.tar.gz", hash = "sha256:65f0d98a3786ec729ba9b5f70448895b2d1d1f27949aa7af5cb4f39da341bbc4", size = 112826, upload-time = "2025-12-05T20:27:14.931Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/fe/3363024b6dda5968401f45d8b345ed95ce4fd536d58f799988b4b28184ad/boto3-1.42.47.tar.gz", hash = "sha256:74812a2e29de7c2bd19e446d765cb887394f20f1517388484b51891a410f33b2", size = 112884, upload-time = "2026-02-11T20:49:49.196Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/25/9ae819385aad79f524859f7179cecf8ac019b63ac8f150c51b250967f6db/boto3-1.42.4-py3-none-any.whl", hash = "sha256:0f4089e230d55f981d67376e48cefd41c3d58c7f694480f13288e6ff7b1fefbc", size = 140621, upload-time = "2025-12-05T20:27:12.803Z" }, + { url = "https://files.pythonhosted.org/packages/47/7b/884e30adab2339ce5cce7b800f5fa619254d36e89e50a8cf39a5524edc35/boto3-1.42.47-py3-none-any.whl", hash = "sha256:ed881ed246027028af566acbb80f008aa619be4d3fdbcc4ad3c75dbe8c34bfaf", size = 140608, upload-time = "2026-02-11T20:49:47.664Z" }, ] [[package]] name = "botocore" -version = "1.42.4" +version = "1.42.47" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/b7/dec048c124619b2702b5236c5fc9d8e5b0a87013529e9245dc49aaaf31ff/botocore-1.42.4.tar.gz", hash = "sha256:d4816023492b987a804f693c2d76fb751fdc8755d49933106d69e2489c4c0f98", size = 14848605, upload-time = "2025-12-05T20:27:02.919Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/a6/d15f5dfe990abd76dbdb2105a7697e0d948e04c41dfd97c058bc76c7cebd/botocore-1.42.47.tar.gz", hash = "sha256:c26e190c1b4d863ba7b44dc68cc574d8eb862ddae5f0fe3472801daee12a0378", size = 14952255, upload-time = "2026-02-11T20:49:40.157Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/a2/7b50f12a9c5a33cd85a5f23fdf78a0cbc445c0245c16051bb627f328be06/botocore-1.42.4-py3-none-any.whl", hash = "sha256:c3b091fd33809f187824b6434e518b889514ded5164cb379358367c18e8b0d7d", size = 14519938, upload-time = "2025-12-05T20:26:58.881Z" }, + { url = "https://files.pythonhosted.org/packages/54/5e/50e3a59b243894088eeb949a654fb21d9ab7d0d703034470de016828d85a/botocore-1.42.47-py3-none-any.whl", hash = "sha256:c60f5feaf189423e17755aca3f1d672b7466620dd2032440b32aaac64ae8cac8", size = 14625351, upload-time = "2026-02-11T20:49:36.143Z" }, ] [[package]] @@ -283,11 +303,11 @@ wheels = [ [[package]] name = "certifi" -version = "2025.11.12" +version = "2026.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, ] [[package]] @@ -411,119 +431,124 @@ wheels = [ [[package]] name = "coverage" -version = "7.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/89/26/4a96807b193b011588099c3b5c89fbb05294e5b90e71018e065465f34eb6/coverage-7.12.0.tar.gz", hash = "sha256:fc11e0a4e372cb5f282f16ef90d4a585034050ccda536451901abfb19a57f40c", size = 819341, upload-time = "2025-11-18T13:34:20.766Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/14/771700b4048774e48d2c54ed0c674273702713c9ee7acdfede40c2666747/coverage-7.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47324fffca8d8eae7e185b5bb20c14645f23350f870c1649003618ea91a78941", size = 217725, upload-time = "2025-11-18T13:32:49.22Z" }, - { url = "https://files.pythonhosted.org/packages/17/a7/3aa4144d3bcb719bf67b22d2d51c2d577bf801498c13cb08f64173e80497/coverage-7.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ccf3b2ede91decd2fb53ec73c1f949c3e034129d1e0b07798ff1d02ea0c8fa4a", size = 218098, upload-time = "2025-11-18T13:32:50.78Z" }, - { url = "https://files.pythonhosted.org/packages/fc/9c/b846bbc774ff81091a12a10203e70562c91ae71badda00c5ae5b613527b1/coverage-7.12.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b365adc70a6936c6b0582dc38746b33b2454148c02349345412c6e743efb646d", size = 249093, upload-time = "2025-11-18T13:32:52.554Z" }, - { url = "https://files.pythonhosted.org/packages/76/b6/67d7c0e1f400b32c883e9342de4a8c2ae7c1a0b57c5de87622b7262e2309/coverage-7.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc13baf85cd8a4cfcf4a35c7bc9d795837ad809775f782f697bf630b7e200211", size = 251686, upload-time = "2025-11-18T13:32:54.862Z" }, - { url = "https://files.pythonhosted.org/packages/cc/75/b095bd4b39d49c3be4bffbb3135fea18a99a431c52dd7513637c0762fecb/coverage-7.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:099d11698385d572ceafb3288a5b80fe1fc58bf665b3f9d362389de488361d3d", size = 252930, upload-time = "2025-11-18T13:32:56.417Z" }, - { url = "https://files.pythonhosted.org/packages/6e/f3/466f63015c7c80550bead3093aacabf5380c1220a2a93c35d374cae8f762/coverage-7.12.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:473dc45d69694069adb7680c405fb1e81f60b2aff42c81e2f2c3feaf544d878c", size = 249296, upload-time = "2025-11-18T13:32:58.074Z" }, - { url = "https://files.pythonhosted.org/packages/27/86/eba2209bf2b7e28c68698fc13437519a295b2d228ba9e0ec91673e09fa92/coverage-7.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:583f9adbefd278e9de33c33d6846aa8f5d164fa49b47144180a0e037f0688bb9", size = 251068, upload-time = "2025-11-18T13:32:59.646Z" }, - { url = "https://files.pythonhosted.org/packages/ec/55/ca8ae7dbba962a3351f18940b359b94c6bafdd7757945fdc79ec9e452dc7/coverage-7.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2089cc445f2dc0af6f801f0d1355c025b76c24481935303cf1af28f636688f0", size = 249034, upload-time = "2025-11-18T13:33:01.481Z" }, - { url = "https://files.pythonhosted.org/packages/7a/d7/39136149325cad92d420b023b5fd900dabdd1c3a0d1d5f148ef4a8cedef5/coverage-7.12.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:950411f1eb5d579999c5f66c62a40961f126fc71e5e14419f004471957b51508", size = 248853, upload-time = "2025-11-18T13:33:02.935Z" }, - { url = "https://files.pythonhosted.org/packages/fe/b6/76e1add8b87ef60e00643b0b7f8f7bb73d4bf5249a3be19ebefc5793dd25/coverage-7.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1aab7302a87bafebfe76b12af681b56ff446dc6f32ed178ff9c092ca776e6bc", size = 250619, upload-time = "2025-11-18T13:33:04.336Z" }, - { url = "https://files.pythonhosted.org/packages/95/87/924c6dc64f9203f7a3c1832a6a0eee5a8335dbe5f1bdadcc278d6f1b4d74/coverage-7.12.0-cp313-cp313-win32.whl", hash = "sha256:d7e0d0303c13b54db495eb636bc2465b2fb8475d4c8bcec8fe4b5ca454dfbae8", size = 220261, upload-time = "2025-11-18T13:33:06.493Z" }, - { url = "https://files.pythonhosted.org/packages/91/77/dd4aff9af16ff776bf355a24d87eeb48fc6acde54c907cc1ea89b14a8804/coverage-7.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:ce61969812d6a98a981d147d9ac583a36ac7db7766f2e64a9d4d059c2fe29d07", size = 221072, upload-time = "2025-11-18T13:33:07.926Z" }, - { url = "https://files.pythonhosted.org/packages/70/49/5c9dc46205fef31b1b226a6e16513193715290584317fd4df91cdaf28b22/coverage-7.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bcec6f47e4cb8a4c2dc91ce507f6eefc6a1b10f58df32cdc61dff65455031dfc", size = 219702, upload-time = "2025-11-18T13:33:09.631Z" }, - { url = "https://files.pythonhosted.org/packages/9b/62/f87922641c7198667994dd472a91e1d9b829c95d6c29529ceb52132436ad/coverage-7.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:459443346509476170d553035e4a3eed7b860f4fe5242f02de1010501956ce87", size = 218420, upload-time = "2025-11-18T13:33:11.153Z" }, - { url = "https://files.pythonhosted.org/packages/85/dd/1cc13b2395ef15dbb27d7370a2509b4aee77890a464fb35d72d428f84871/coverage-7.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04a79245ab2b7a61688958f7a855275997134bc84f4a03bc240cf64ff132abf6", size = 218773, upload-time = "2025-11-18T13:33:12.569Z" }, - { url = "https://files.pythonhosted.org/packages/74/40/35773cc4bb1e9d4658d4fb669eb4195b3151bef3bbd6f866aba5cd5dac82/coverage-7.12.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:09a86acaaa8455f13d6a99221d9654df249b33937b4e212b4e5a822065f12aa7", size = 260078, upload-time = "2025-11-18T13:33:14.037Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ee/231bb1a6ffc2905e396557585ebc6bdc559e7c66708376d245a1f1d330fc/coverage-7.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:907e0df1b71ba77463687a74149c6122c3f6aac56c2510a5d906b2f368208560", size = 262144, upload-time = "2025-11-18T13:33:15.601Z" }, - { url = "https://files.pythonhosted.org/packages/28/be/32f4aa9f3bf0b56f3971001b56508352c7753915345d45fab4296a986f01/coverage-7.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b57e2d0ddd5f0582bae5437c04ee71c46cd908e7bc5d4d0391f9a41e812dd12", size = 264574, upload-time = "2025-11-18T13:33:17.354Z" }, - { url = "https://files.pythonhosted.org/packages/68/7c/00489fcbc2245d13ab12189b977e0cf06ff3351cb98bc6beba8bd68c5902/coverage-7.12.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:58c1c6aa677f3a1411fe6fb28ec3a942e4f665df036a3608816e0847fad23296", size = 259298, upload-time = "2025-11-18T13:33:18.958Z" }, - { url = "https://files.pythonhosted.org/packages/96/b4/f0760d65d56c3bea95b449e02570d4abd2549dc784bf39a2d4721a2d8ceb/coverage-7.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4c589361263ab2953e3c4cd2a94db94c4ad4a8e572776ecfbad2389c626e4507", size = 262150, upload-time = "2025-11-18T13:33:20.644Z" }, - { url = "https://files.pythonhosted.org/packages/c5/71/9a9314df00f9326d78c1e5a910f520d599205907432d90d1c1b7a97aa4b1/coverage-7.12.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:91b810a163ccad2e43b1faa11d70d3cf4b6f3d83f9fd5f2df82a32d47b648e0d", size = 259763, upload-time = "2025-11-18T13:33:22.189Z" }, - { url = "https://files.pythonhosted.org/packages/10/34/01a0aceed13fbdf925876b9a15d50862eb8845454301fe3cdd1df08b2182/coverage-7.12.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:40c867af715f22592e0d0fb533a33a71ec9e0f73a6945f722a0c85c8c1cbe3a2", size = 258653, upload-time = "2025-11-18T13:33:24.239Z" }, - { url = "https://files.pythonhosted.org/packages/8d/04/81d8fd64928acf1574bbb0181f66901c6c1c6279c8ccf5f84259d2c68ae9/coverage-7.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:68b0d0a2d84f333de875666259dadf28cc67858bc8fd8b3f1eae84d3c2bec455", size = 260856, upload-time = "2025-11-18T13:33:26.365Z" }, - { url = "https://files.pythonhosted.org/packages/f2/76/fa2a37bfaeaf1f766a2d2360a25a5297d4fb567098112f6517475eee120b/coverage-7.12.0-cp313-cp313t-win32.whl", hash = "sha256:73f9e7fbd51a221818fd11b7090eaa835a353ddd59c236c57b2199486b116c6d", size = 220936, upload-time = "2025-11-18T13:33:28.165Z" }, - { url = "https://files.pythonhosted.org/packages/f9/52/60f64d932d555102611c366afb0eb434b34266b1d9266fc2fe18ab641c47/coverage-7.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:24cff9d1f5743f67db7ba46ff284018a6e9aeb649b67aa1e70c396aa1b7cb23c", size = 222001, upload-time = "2025-11-18T13:33:29.656Z" }, - { url = "https://files.pythonhosted.org/packages/77/df/c303164154a5a3aea7472bf323b7c857fed93b26618ed9fc5c2955566bb0/coverage-7.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c87395744f5c77c866d0f5a43d97cc39e17c7f1cb0115e54a2fe67ca75c5d14d", size = 220273, upload-time = "2025-11-18T13:33:31.415Z" }, - { url = "https://files.pythonhosted.org/packages/bf/2e/fc12db0883478d6e12bbd62d481210f0c8daf036102aa11434a0c5755825/coverage-7.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a1c59b7dc169809a88b21a936eccf71c3895a78f5592051b1af8f4d59c2b4f92", size = 217777, upload-time = "2025-11-18T13:33:32.86Z" }, - { url = "https://files.pythonhosted.org/packages/1f/c1/ce3e525d223350c6ec16b9be8a057623f54226ef7f4c2fee361ebb6a02b8/coverage-7.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8787b0f982e020adb732b9f051f3e49dd5054cebbc3f3432061278512a2b1360", size = 218100, upload-time = "2025-11-18T13:33:34.532Z" }, - { url = "https://files.pythonhosted.org/packages/15/87/113757441504aee3808cb422990ed7c8bcc2d53a6779c66c5adef0942939/coverage-7.12.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ea5a9f7dc8877455b13dd1effd3202e0bca72f6f3ab09f9036b1bcf728f69ac", size = 249151, upload-time = "2025-11-18T13:33:36.135Z" }, - { url = "https://files.pythonhosted.org/packages/d9/1d/9529d9bd44049b6b05bb319c03a3a7e4b0a8a802d28fa348ad407e10706d/coverage-7.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fdba9f15849534594f60b47c9a30bc70409b54947319a7c4fd0e8e3d8d2f355d", size = 251667, upload-time = "2025-11-18T13:33:37.996Z" }, - { url = "https://files.pythonhosted.org/packages/11/bb/567e751c41e9c03dc29d3ce74b8c89a1e3396313e34f255a2a2e8b9ebb56/coverage-7.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a00594770eb715854fb1c57e0dea08cce6720cfbc531accdb9850d7c7770396c", size = 253003, upload-time = "2025-11-18T13:33:39.553Z" }, - { url = "https://files.pythonhosted.org/packages/e4/b3/c2cce2d8526a02fb9e9ca14a263ca6fc074449b33a6afa4892838c903528/coverage-7.12.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5560c7e0d82b42eb1951e4f68f071f8017c824ebfd5a6ebe42c60ac16c6c2434", size = 249185, upload-time = "2025-11-18T13:33:42.086Z" }, - { url = "https://files.pythonhosted.org/packages/0e/a7/967f93bb66e82c9113c66a8d0b65ecf72fc865adfba5a145f50c7af7e58d/coverage-7.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2e26b481c9159c2773a37947a9718cfdc58893029cdfb177531793e375cfc", size = 251025, upload-time = "2025-11-18T13:33:43.634Z" }, - { url = "https://files.pythonhosted.org/packages/b9/b2/f2f6f56337bc1af465d5b2dc1ee7ee2141b8b9272f3bf6213fcbc309a836/coverage-7.12.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6e1a8c066dabcde56d5d9fed6a66bc19a2883a3fe051f0c397a41fc42aedd4cc", size = 248979, upload-time = "2025-11-18T13:33:46.04Z" }, - { url = "https://files.pythonhosted.org/packages/f4/7a/bf4209f45a4aec09d10a01a57313a46c0e0e8f4c55ff2965467d41a92036/coverage-7.12.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f7ba9da4726e446d8dd8aae5a6cd872511184a5d861de80a86ef970b5dacce3e", size = 248800, upload-time = "2025-11-18T13:33:47.546Z" }, - { url = "https://files.pythonhosted.org/packages/b8/b7/1e01b8696fb0521810f60c5bbebf699100d6754183e6cc0679bf2ed76531/coverage-7.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e0f483ab4f749039894abaf80c2f9e7ed77bbf3c737517fb88c8e8e305896a17", size = 250460, upload-time = "2025-11-18T13:33:49.537Z" }, - { url = "https://files.pythonhosted.org/packages/71/ae/84324fb9cb46c024760e706353d9b771a81b398d117d8c1fe010391c186f/coverage-7.12.0-cp314-cp314-win32.whl", hash = "sha256:76336c19a9ef4a94b2f8dc79f8ac2da3f193f625bb5d6f51a328cd19bfc19933", size = 220533, upload-time = "2025-11-18T13:33:51.16Z" }, - { url = "https://files.pythonhosted.org/packages/e2/71/1033629deb8460a8f97f83e6ac4ca3b93952e2b6f826056684df8275e015/coverage-7.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c1059b600aec6ef090721f8f633f60ed70afaffe8ecab85b59df748f24b31fe", size = 221348, upload-time = "2025-11-18T13:33:52.776Z" }, - { url = "https://files.pythonhosted.org/packages/0a/5f/ac8107a902f623b0c251abdb749be282dc2ab61854a8a4fcf49e276fce2f/coverage-7.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:172cf3a34bfef42611963e2b661302a8931f44df31629e5b1050567d6b90287d", size = 219922, upload-time = "2025-11-18T13:33:54.316Z" }, - { url = "https://files.pythonhosted.org/packages/79/6e/f27af2d4da367f16077d21ef6fe796c874408219fa6dd3f3efe7751bd910/coverage-7.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:aa7d48520a32cb21c7a9b31f81799e8eaec7239db36c3b670be0fa2403828d1d", size = 218511, upload-time = "2025-11-18T13:33:56.343Z" }, - { url = "https://files.pythonhosted.org/packages/67/dd/65fd874aa460c30da78f9d259400d8e6a4ef457d61ab052fd248f0050558/coverage-7.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:90d58ac63bc85e0fb919f14d09d6caa63f35a5512a2205284b7816cafd21bb03", size = 218771, upload-time = "2025-11-18T13:33:57.966Z" }, - { url = "https://files.pythonhosted.org/packages/55/e0/7c6b71d327d8068cb79c05f8f45bf1b6145f7a0de23bbebe63578fe5240a/coverage-7.12.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca8ecfa283764fdda3eae1bdb6afe58bf78c2c3ec2b2edcb05a671f0bba7b3f9", size = 260151, upload-time = "2025-11-18T13:33:59.597Z" }, - { url = "https://files.pythonhosted.org/packages/49/ce/4697457d58285b7200de6b46d606ea71066c6e674571a946a6ea908fb588/coverage-7.12.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:874fe69a0785d96bd066059cd4368022cebbec1a8958f224f0016979183916e6", size = 262257, upload-time = "2025-11-18T13:34:01.166Z" }, - { url = "https://files.pythonhosted.org/packages/2f/33/acbc6e447aee4ceba88c15528dbe04a35fb4d67b59d393d2e0d6f1e242c1/coverage-7.12.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3c889c0b8b283a24d721a9eabc8ccafcfc3aebf167e4cd0d0e23bf8ec4e339", size = 264671, upload-time = "2025-11-18T13:34:02.795Z" }, - { url = "https://files.pythonhosted.org/packages/87/ec/e2822a795c1ed44d569980097be839c5e734d4c0c1119ef8e0a073496a30/coverage-7.12.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bb5b894b3ec09dcd6d3743229dc7f2c42ef7787dc40596ae04c0edda487371e", size = 259231, upload-time = "2025-11-18T13:34:04.397Z" }, - { url = "https://files.pythonhosted.org/packages/72/c5/a7ec5395bb4a49c9b7ad97e63f0c92f6bf4a9e006b1393555a02dae75f16/coverage-7.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:79a44421cd5fba96aa57b5e3b5a4d3274c449d4c622e8f76882d76635501fd13", size = 262137, upload-time = "2025-11-18T13:34:06.068Z" }, - { url = "https://files.pythonhosted.org/packages/67/0c/02c08858b764129f4ecb8e316684272972e60777ae986f3865b10940bdd6/coverage-7.12.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:33baadc0efd5c7294f436a632566ccc1f72c867f82833eb59820ee37dc811c6f", size = 259745, upload-time = "2025-11-18T13:34:08.04Z" }, - { url = "https://files.pythonhosted.org/packages/5a/04/4fd32b7084505f3829a8fe45c1a74a7a728cb251aaadbe3bec04abcef06d/coverage-7.12.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c406a71f544800ef7e9e0000af706b88465f3573ae8b8de37e5f96c59f689ad1", size = 258570, upload-time = "2025-11-18T13:34:09.676Z" }, - { url = "https://files.pythonhosted.org/packages/48/35/2365e37c90df4f5342c4fa202223744119fe31264ee2924f09f074ea9b6d/coverage-7.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e71bba6a40883b00c6d571599b4627f50c360b3d0d02bfc658168936be74027b", size = 260899, upload-time = "2025-11-18T13:34:11.259Z" }, - { url = "https://files.pythonhosted.org/packages/05/56/26ab0464ca733fa325e8e71455c58c1c374ce30f7c04cebb88eabb037b18/coverage-7.12.0-cp314-cp314t-win32.whl", hash = "sha256:9157a5e233c40ce6613dead4c131a006adfda70e557b6856b97aceed01b0e27a", size = 221313, upload-time = "2025-11-18T13:34:12.863Z" }, - { url = "https://files.pythonhosted.org/packages/da/1c/017a3e1113ed34d998b27d2c6dba08a9e7cb97d362f0ec988fcd873dcf81/coverage-7.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e84da3a0fd233aeec797b981c51af1cabac74f9bd67be42458365b30d11b5291", size = 222423, upload-time = "2025-11-18T13:34:15.14Z" }, - { url = "https://files.pythonhosted.org/packages/4c/36/bcc504fdd5169301b52568802bb1b9cdde2e27a01d39fbb3b4b508ab7c2c/coverage-7.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:01d24af36fedda51c2b1aca56e4330a3710f83b02a5ff3743a6b015ffa7c9384", size = 220459, upload-time = "2025-11-18T13:34:17.222Z" }, - { url = "https://files.pythonhosted.org/packages/ce/a3/43b749004e3c09452e39bb56347a008f0a0668aad37324a99b5c8ca91d9e/coverage-7.12.0-py3-none-any.whl", hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a", size = 209503, upload-time = "2025-11-18T13:34:18.892Z" }, +version = "7.13.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/56/95b7e30fa389756cb56630faa728da46a27b8c6eb46f9d557c68fff12b65/coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91", size = 827239, upload-time = "2026-02-09T12:59:03.86Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/23/aad45061a31677d68e47499197a131eea55da4875d16c1f42021ab963503/coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b66a2da594b6068b48b2692f043f35d4d3693fb639d5ea8b39533c2ad9ac3ab9", size = 219474, upload-time = "2026-02-09T12:57:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/a5/70/9b8b67a0945f3dfec1fd896c5cefb7c19d5a3a6d74630b99a895170999ae/coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3599eb3992d814d23b35c536c28df1a882caa950f8f507cef23d1cbf334995ac", size = 219844, upload-time = "2026-02-09T12:57:20.66Z" }, + { url = "https://files.pythonhosted.org/packages/97/fd/7e859f8fab324cef6c4ad7cff156ca7c489fef9179d5749b0c8d321281c2/coverage-7.13.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93550784d9281e374fb5a12bf1324cc8a963fd63b2d2f223503ef0fd4aa339ea", size = 250832, upload-time = "2026-02-09T12:57:22.007Z" }, + { url = "https://files.pythonhosted.org/packages/e4/dc/b2442d10020c2f52617828862d8b6ee337859cd8f3a1f13d607dddda9cf7/coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b720ce6a88a2755f7c697c23268ddc47a571b88052e6b155224347389fdf6a3b", size = 253434, upload-time = "2026-02-09T12:57:23.339Z" }, + { url = "https://files.pythonhosted.org/packages/5a/88/6728a7ad17428b18d836540630487231f5470fb82454871149502f5e5aa2/coverage-7.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b322db1284a2ed3aa28ffd8ebe3db91c929b7a333c0820abec3d838ef5b3525", size = 254676, upload-time = "2026-02-09T12:57:24.774Z" }, + { url = "https://files.pythonhosted.org/packages/7c/bc/21244b1b8cedf0dff0a2b53b208015fe798d5f2a8d5348dbfece04224fff/coverage-7.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4594c67d8a7c89cf922d9df0438c7c7bb022ad506eddb0fdb2863359ff78242", size = 256807, upload-time = "2026-02-09T12:57:26.125Z" }, + { url = "https://files.pythonhosted.org/packages/97/a0/ddba7ed3251cff51006737a727d84e05b61517d1784a9988a846ba508877/coverage-7.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53d133df809c743eb8bce33b24bcababb371f4441340578cd406e084d94a6148", size = 251058, upload-time = "2026-02-09T12:57:27.614Z" }, + { url = "https://files.pythonhosted.org/packages/9b/55/e289addf7ff54d3a540526f33751951bf0878f3809b47f6dfb3def69c6f7/coverage-7.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76451d1978b95ba6507a039090ba076105c87cc76fc3efd5d35d72093964d49a", size = 252805, upload-time = "2026-02-09T12:57:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/13/4e/cc276b1fa4a59be56d96f1dabddbdc30f4ba22e3b1cd42504c37b3313255/coverage-7.13.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f57b33491e281e962021de110b451ab8a24182589be17e12a22c79047935e23", size = 250766, upload-time = "2026-02-09T12:57:30.522Z" }, + { url = "https://files.pythonhosted.org/packages/94/44/1093b8f93018f8b41a8cf29636c9292502f05e4a113d4d107d14a3acd044/coverage-7.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1731dc33dc276dafc410a885cbf5992f1ff171393e48a21453b78727d090de80", size = 254923, upload-time = "2026-02-09T12:57:31.946Z" }, + { url = "https://files.pythonhosted.org/packages/8b/55/ea2796da2d42257f37dbea1aab239ba9263b31bd91d5527cdd6db5efe174/coverage-7.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:bd60d4fe2f6fa7dff9223ca1bbc9f05d2b6697bc5961072e5d3b952d46e1b1ea", size = 250591, upload-time = "2026-02-09T12:57:33.842Z" }, + { url = "https://files.pythonhosted.org/packages/d4/fa/7c4bb72aacf8af5020675aa633e59c1fbe296d22aed191b6a5b711eb2bc7/coverage-7.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9181a3ccead280b828fae232df12b16652702b49d41e99d657f46cc7b1f6ec7a", size = 252364, upload-time = "2026-02-09T12:57:35.743Z" }, + { url = "https://files.pythonhosted.org/packages/5c/38/a8d2ec0146479c20bbaa7181b5b455a0c41101eed57f10dd19a78ab44c80/coverage-7.13.4-cp313-cp313-win32.whl", hash = "sha256:f53d492307962561ac7de4cd1de3e363589b000ab69617c6156a16ba7237998d", size = 222010, upload-time = "2026-02-09T12:57:37.25Z" }, + { url = "https://files.pythonhosted.org/packages/e2/0c/dbfafbe90a185943dcfbc766fe0e1909f658811492d79b741523a414a6cc/coverage-7.13.4-cp313-cp313-win_amd64.whl", hash = "sha256:e6f70dec1cc557e52df5306d051ef56003f74d56e9c4dd7ddb07e07ef32a84dd", size = 222818, upload-time = "2026-02-09T12:57:38.734Z" }, + { url = "https://files.pythonhosted.org/packages/04/d1/934918a138c932c90d78301f45f677fb05c39a3112b96fd2c8e60503cdc7/coverage-7.13.4-cp313-cp313-win_arm64.whl", hash = "sha256:fb07dc5da7e849e2ad31a5d74e9bece81f30ecf5a42909d0a695f8bd1874d6af", size = 221438, upload-time = "2026-02-09T12:57:40.223Z" }, + { url = "https://files.pythonhosted.org/packages/52/57/ee93ced533bcb3e6df961c0c6e42da2fc6addae53fb95b94a89b1e33ebd7/coverage-7.13.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40d74da8e6c4b9ac18b15331c4b5ebc35a17069410cad462ad4f40dcd2d50c0d", size = 220165, upload-time = "2026-02-09T12:57:41.639Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/969fc285a6fbdda49d91af278488d904dcd7651b2693872f0ff94e40e84a/coverage-7.13.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4223b4230a376138939a9173f1bdd6521994f2aff8047fae100d6d94d50c5a12", size = 220516, upload-time = "2026-02-09T12:57:44.215Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b8/9531944e16267e2735a30a9641ff49671f07e8138ecf1ca13db9fd2560c7/coverage-7.13.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1d4be36a5114c499f9f1f9195e95ebf979460dbe2d88e6816ea202010ba1c34b", size = 261804, upload-time = "2026-02-09T12:57:45.989Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f3/e63df6d500314a2a60390d1989240d5f27318a7a68fa30ad3806e2a9323e/coverage-7.13.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:200dea7d1e8095cc6e98cdabe3fd1d21ab17d3cee6dab00cadbb2fe35d9c15b9", size = 263885, upload-time = "2026-02-09T12:57:47.42Z" }, + { url = "https://files.pythonhosted.org/packages/f3/67/7654810de580e14b37670b60a09c599fa348e48312db5b216d730857ffe6/coverage-7.13.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8eb931ee8e6d8243e253e5ed7336deea6904369d2fd8ae6e43f68abbf167092", size = 266308, upload-time = "2026-02-09T12:57:49.345Z" }, + { url = "https://files.pythonhosted.org/packages/37/6f/39d41eca0eab3cc82115953ad41c4e77935286c930e8fad15eaed1389d83/coverage-7.13.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75eab1ebe4f2f64d9509b984f9314d4aa788540368218b858dad56dc8f3e5eb9", size = 267452, upload-time = "2026-02-09T12:57:50.811Z" }, + { url = "https://files.pythonhosted.org/packages/50/6d/39c0fbb8fc5cd4d2090811e553c2108cf5112e882f82505ee7495349a6bf/coverage-7.13.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c35eb28c1d085eb7d8c9b3296567a1bebe03ce72962e932431b9a61f28facf26", size = 261057, upload-time = "2026-02-09T12:57:52.447Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a2/60010c669df5fa603bb5a97fb75407e191a846510da70ac657eb696b7fce/coverage-7.13.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb88b316ec33760714a4720feb2816a3a59180fd58c1985012054fa7aebee4c2", size = 263875, upload-time = "2026-02-09T12:57:53.938Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d9/63b22a6bdbd17f1f96e9ed58604c2a6b0e72a9133e37d663bef185877cf6/coverage-7.13.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7d41eead3cc673cbd38a4417deb7fd0b4ca26954ff7dc6078e33f6ff97bed940", size = 261500, upload-time = "2026-02-09T12:57:56.012Z" }, + { url = "https://files.pythonhosted.org/packages/70/bf/69f86ba1ad85bc3ad240e4c0e57a2e620fbc0e1645a47b5c62f0e941ad7f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:fb26a934946a6afe0e326aebe0730cdff393a8bc0bbb65a2f41e30feddca399c", size = 265212, upload-time = "2026-02-09T12:57:57.5Z" }, + { url = "https://files.pythonhosted.org/packages/ae/f2/5f65a278a8c2148731831574c73e42f57204243d33bedaaf18fa79c5958f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:dae88bc0fc77edaa65c14be099bd57ee140cf507e6bfdeea7938457ab387efb0", size = 260398, upload-time = "2026-02-09T12:57:59.027Z" }, + { url = "https://files.pythonhosted.org/packages/ef/80/6e8280a350ee9fea92f14b8357448a242dcaa243cb2c72ab0ca591f66c8c/coverage-7.13.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:845f352911777a8e722bfce168958214951e07e47e5d5d9744109fa5fe77f79b", size = 262584, upload-time = "2026-02-09T12:58:01.129Z" }, + { url = "https://files.pythonhosted.org/packages/22/63/01ff182fc95f260b539590fb12c11ad3e21332c15f9799cb5e2386f71d9f/coverage-7.13.4-cp313-cp313t-win32.whl", hash = "sha256:2fa8d5f8de70688a28240de9e139fa16b153cc3cbb01c5f16d88d6505ebdadf9", size = 222688, upload-time = "2026-02-09T12:58:02.736Z" }, + { url = "https://files.pythonhosted.org/packages/a9/43/89de4ef5d3cd53b886afa114065f7e9d3707bdb3e5efae13535b46ae483d/coverage-7.13.4-cp313-cp313t-win_amd64.whl", hash = "sha256:9351229c8c8407645840edcc277f4a2d44814d1bc34a2128c11c2a031d45a5dd", size = 223746, upload-time = "2026-02-09T12:58:05.362Z" }, + { url = "https://files.pythonhosted.org/packages/35/39/7cf0aa9a10d470a5309b38b289b9bb07ddeac5d61af9b664fe9775a4cb3e/coverage-7.13.4-cp313-cp313t-win_arm64.whl", hash = "sha256:30b8d0512f2dc8c8747557e8fb459d6176a2c9e5731e2b74d311c03b78451997", size = 222003, upload-time = "2026-02-09T12:58:06.952Z" }, + { url = "https://files.pythonhosted.org/packages/92/11/a9cf762bb83386467737d32187756a42094927150c3e107df4cb078e8590/coverage-7.13.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:300deaee342f90696ed186e3a00c71b5b3d27bffe9e827677954f4ee56969601", size = 219522, upload-time = "2026-02-09T12:58:08.623Z" }, + { url = "https://files.pythonhosted.org/packages/d3/28/56e6d892b7b052236d67c95f1936b6a7cf7c3e2634bf27610b8cbd7f9c60/coverage-7.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29e3220258d682b6226a9b0925bc563ed9a1ebcff3cad30f043eceea7eaf2689", size = 219855, upload-time = "2026-02-09T12:58:10.176Z" }, + { url = "https://files.pythonhosted.org/packages/e5/69/233459ee9eb0c0d10fcc2fe425a029b3fa5ce0f040c966ebce851d030c70/coverage-7.13.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:391ee8f19bef69210978363ca930f7328081c6a0152f1166c91f0b5fdd2a773c", size = 250887, upload-time = "2026-02-09T12:58:12.503Z" }, + { url = "https://files.pythonhosted.org/packages/06/90/2cdab0974b9b5bbc1623f7876b73603aecac11b8d95b85b5b86b32de5eab/coverage-7.13.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0dd7ab8278f0d58a0128ba2fca25824321f05d059c1441800e934ff2efa52129", size = 253396, upload-time = "2026-02-09T12:58:14.615Z" }, + { url = "https://files.pythonhosted.org/packages/ac/15/ea4da0f85bf7d7b27635039e649e99deb8173fe551096ea15017f7053537/coverage-7.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78cdf0d578b15148b009ccf18c686aa4f719d887e76e6b40c38ffb61d264a552", size = 254745, upload-time = "2026-02-09T12:58:16.162Z" }, + { url = "https://files.pythonhosted.org/packages/99/11/bb356e86920c655ca4d61daee4e2bbc7258f0a37de0be32d233b561134ff/coverage-7.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:48685fee12c2eb3b27c62f2658e7ea21e9c3239cba5a8a242801a0a3f6a8c62a", size = 257055, upload-time = "2026-02-09T12:58:17.892Z" }, + { url = "https://files.pythonhosted.org/packages/c9/0f/9ae1f8cb17029e09da06ca4e28c9e1d5c1c0a511c7074592e37e0836c915/coverage-7.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4e83efc079eb39480e6346a15a1bcb3e9b04759c5202d157e1dd4303cd619356", size = 250911, upload-time = "2026-02-09T12:58:19.495Z" }, + { url = "https://files.pythonhosted.org/packages/89/3a/adfb68558fa815cbc29747b553bc833d2150228f251b127f1ce97e48547c/coverage-7.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecae9737b72408d6a950f7e525f30aca12d4bd8dd95e37342e5beb3a2a8c4f71", size = 252754, upload-time = "2026-02-09T12:58:21.064Z" }, + { url = "https://files.pythonhosted.org/packages/32/b1/540d0c27c4e748bd3cd0bd001076ee416eda993c2bae47a73b7cc9357931/coverage-7.13.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ae4578f8528569d3cf303fef2ea569c7f4c4059a38c8667ccef15c6e1f118aa5", size = 250720, upload-time = "2026-02-09T12:58:22.622Z" }, + { url = "https://files.pythonhosted.org/packages/c7/95/383609462b3ffb1fe133014a7c84fc0dd01ed55ac6140fa1093b5af7ebb1/coverage-7.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6fdef321fdfbb30a197efa02d48fcd9981f0d8ad2ae8903ac318adc653f5df98", size = 254994, upload-time = "2026-02-09T12:58:24.548Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ba/1761138e86c81680bfc3c49579d66312865457f9fe405b033184e5793cb3/coverage-7.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b0f6ccf3dbe577170bebfce1318707d0e8c3650003cb4b3a9dd744575daa8b5", size = 250531, upload-time = "2026-02-09T12:58:26.271Z" }, + { url = "https://files.pythonhosted.org/packages/f8/8e/05900df797a9c11837ab59c4d6fe94094e029582aab75c3309a93e6fb4e3/coverage-7.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75fcd519f2a5765db3f0e391eb3b7d150cce1a771bf4c9f861aeab86c767a3c0", size = 252189, upload-time = "2026-02-09T12:58:27.807Z" }, + { url = "https://files.pythonhosted.org/packages/00/bd/29c9f2db9ea4ed2738b8a9508c35626eb205d51af4ab7bf56a21a2e49926/coverage-7.13.4-cp314-cp314-win32.whl", hash = "sha256:8e798c266c378da2bd819b0677df41ab46d78065fb2a399558f3f6cae78b2fbb", size = 222258, upload-time = "2026-02-09T12:58:29.441Z" }, + { url = "https://files.pythonhosted.org/packages/a7/4d/1f8e723f6829977410efeb88f73673d794075091c8c7c18848d273dc9d73/coverage-7.13.4-cp314-cp314-win_amd64.whl", hash = "sha256:245e37f664d89861cf2329c9afa2c1fe9e6d4e1a09d872c947e70718aeeac505", size = 223073, upload-time = "2026-02-09T12:58:31.026Z" }, + { url = "https://files.pythonhosted.org/packages/51/5b/84100025be913b44e082ea32abcf1afbf4e872f5120b7a1cab1d331b1e13/coverage-7.13.4-cp314-cp314-win_arm64.whl", hash = "sha256:ad27098a189e5838900ce4c2a99f2fe42a0bf0c2093c17c69b45a71579e8d4a2", size = 221638, upload-time = "2026-02-09T12:58:32.599Z" }, + { url = "https://files.pythonhosted.org/packages/a7/e4/c884a405d6ead1370433dad1e3720216b4f9fd8ef5b64bfd984a2a60a11a/coverage-7.13.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:85480adfb35ffc32d40918aad81b89c69c9cc5661a9b8a81476d3e645321a056", size = 220246, upload-time = "2026-02-09T12:58:34.181Z" }, + { url = "https://files.pythonhosted.org/packages/81/5c/4d7ed8b23b233b0fffbc9dfec53c232be2e695468523242ea9fd30f97ad2/coverage-7.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:79be69cf7f3bf9b0deeeb062eab7ac7f36cd4cc4c4dd694bd28921ba4d8596cc", size = 220514, upload-time = "2026-02-09T12:58:35.704Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6f/3284d4203fd2f28edd73034968398cd2d4cb04ab192abc8cff007ea35679/coverage-7.13.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:caa421e2684e382c5d8973ac55e4f36bed6821a9bad5c953494de960c74595c9", size = 261877, upload-time = "2026-02-09T12:58:37.864Z" }, + { url = "https://files.pythonhosted.org/packages/09/aa/b672a647bbe1556a85337dc95bfd40d146e9965ead9cc2fe81bde1e5cbce/coverage-7.13.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14375934243ee05f56c45393fe2ce81fe5cc503c07cee2bdf1725fb8bef3ffaf", size = 264004, upload-time = "2026-02-09T12:58:39.492Z" }, + { url = "https://files.pythonhosted.org/packages/79/a1/aa384dbe9181f98bba87dd23dda436f0c6cf2e148aecbb4e50fc51c1a656/coverage-7.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25a41c3104d08edb094d9db0d905ca54d0cd41c928bb6be3c4c799a54753af55", size = 266408, upload-time = "2026-02-09T12:58:41.852Z" }, + { url = "https://files.pythonhosted.org/packages/53/5e/5150bf17b4019bc600799f376bb9606941e55bd5a775dc1e096b6ffea952/coverage-7.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f01afcff62bf9a08fb32b2c1d6e924236c0383c02c790732b6537269e466a72", size = 267544, upload-time = "2026-02-09T12:58:44.093Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ed/f1de5c675987a4a7a672250d2c5c9d73d289dbf13410f00ed7181d8017dd/coverage-7.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eb9078108fbf0bcdde37c3f4779303673c2fa1fe8f7956e68d447d0dd426d38a", size = 260980, upload-time = "2026-02-09T12:58:45.721Z" }, + { url = "https://files.pythonhosted.org/packages/b3/e3/fe758d01850aa172419a6743fe76ba8b92c29d181d4f676ffe2dae2ba631/coverage-7.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e086334e8537ddd17e5f16a344777c1ab8194986ec533711cbe6c41cde841b6", size = 263871, upload-time = "2026-02-09T12:58:47.334Z" }, + { url = "https://files.pythonhosted.org/packages/b6/76/b829869d464115e22499541def9796b25312b8cf235d3bb00b39f1675395/coverage-7.13.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:725d985c5ab621268b2edb8e50dfe57633dc69bda071abc470fed55a14935fd3", size = 261472, upload-time = "2026-02-09T12:58:48.995Z" }, + { url = "https://files.pythonhosted.org/packages/14/9e/caedb1679e73e2f6ad240173f55218488bfe043e38da577c4ec977489915/coverage-7.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3c06f0f1337c667b971ca2f975523347e63ec5e500b9aa5882d91931cd3ef750", size = 265210, upload-time = "2026-02-09T12:58:51.178Z" }, + { url = "https://files.pythonhosted.org/packages/3a/10/0dd02cb009b16ede425b49ec344aba13a6ae1dc39600840ea6abcb085ac4/coverage-7.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:590c0ed4bf8e85f745e6b805b2e1c457b2e33d5255dd9729743165253bc9ad39", size = 260319, upload-time = "2026-02-09T12:58:53.081Z" }, + { url = "https://files.pythonhosted.org/packages/92/8e/234d2c927af27c6d7a5ffad5bd2cf31634c46a477b4c7adfbfa66baf7ebb/coverage-7.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eb30bf180de3f632cd043322dad5751390e5385108b2807368997d1a92a509d0", size = 262638, upload-time = "2026-02-09T12:58:55.258Z" }, + { url = "https://files.pythonhosted.org/packages/2f/64/e5547c8ff6964e5965c35a480855911b61509cce544f4d442caa759a0702/coverage-7.13.4-cp314-cp314t-win32.whl", hash = "sha256:c4240e7eded42d131a2d2c4dec70374b781b043ddc79a9de4d55ca71f8e98aea", size = 223040, upload-time = "2026-02-09T12:58:56.936Z" }, + { url = "https://files.pythonhosted.org/packages/c7/96/38086d58a181aac86d503dfa9c47eb20715a79c3e3acbdf786e92e5c09a8/coverage-7.13.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4c7d3cc01e7350f2f0f6f7036caaf5673fb56b6998889ccfe9e1c1fe75a9c932", size = 224148, upload-time = "2026-02-09T12:58:58.645Z" }, + { url = "https://files.pythonhosted.org/packages/ce/72/8d10abd3740a0beb98c305e0c3faf454366221c0f37a8bcf8f60020bb65a/coverage-7.13.4-cp314-cp314t-win_arm64.whl", hash = "sha256:23e3f687cf945070d1c90f85db66d11e3025665d8dafa831301a0e0038f3db9b", size = 222172, upload-time = "2026-02-09T12:59:00.396Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4a/331fe2caf6799d591109bb9c08083080f6de90a823695d412a935622abb2/coverage-7.13.4-py3-none-any.whl", hash = "sha256:1af1641e57cf7ba1bd67d677c9abdbcd6cc2ab7da3bca7fa1e2b7e50e65f2ad0", size = 211242, upload-time = "2026-02-09T12:59:02.032Z" }, ] [[package]] name = "cryptography" -version = "46.0.3" +version = "46.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" }, - { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" }, - { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" }, - { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" }, - { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" }, - { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" }, - { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" }, - { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" }, - { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" }, - { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" }, - { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" }, - { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" }, - { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" }, - { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" }, - { url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" }, - { url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" }, - { url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" }, - { url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" }, - { url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" }, - { url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" }, - { url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" }, - { url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" }, - { url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" }, - { url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" }, - { url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" }, - { url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" }, - { url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" }, - { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" }, - { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" }, - { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" }, - { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" }, - { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" }, - { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" }, - { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" }, - { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" }, - { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" }, - { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" }, - { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" }, - { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" }, - { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, + { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, + { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, + { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, + { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, + { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, + { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" }, + { url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287, upload-time = "2026-02-10T19:17:33.801Z" }, + { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, + { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, + { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, + { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" }, + { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, + { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, + { url = "https://files.pythonhosted.org/packages/86/ef/5d00ef966ddd71ac2e6951d278884a84a40ffbd88948ef0e294b214ae9e4/cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", size = 3003637, upload-time = "2026-02-10T19:17:52.997Z" }, + { url = "https://files.pythonhosted.org/packages/b7/57/f3f4160123da6d098db78350fdfd9705057aad21de7388eacb2401dceab9/cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", size = 3469487, upload-time = "2026-02-10T19:17:54.549Z" }, + { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, + { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, + { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, + { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, + { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, + { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, + { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, + { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, + { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, + { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, + { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, + { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" }, + { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, ] [[package]] @@ -536,10 +561,13 @@ wheels = [ ] [[package]] -name = "docopt" -version = "0.6.2" +name = "docopt-ng" +version = "0.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/50/8d6806cf13138127692ae6ff79ddeb4e25eb3b0bcc3c1bd033e7e04531a9/docopt_ng-0.9.0.tar.gz", hash = "sha256:91c6da10b5bb6f2e9e25345829fb8278c78af019f6fc40887ad49b060483b1d7", size = 32264, upload-time = "2023-05-30T20:46:25.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/4a/c3b77fc1a24510b08918b43a473410c0168f6e657118807015f1f1edceea/docopt_ng-0.9.0-py3-none-any.whl", hash = "sha256:bfe4c8b03f9fca424c24ee0b4ffa84bf7391cb18c29ce0f6a8227a3b01b81ff9", size = 16689, upload-time = "2023-05-30T20:46:45.294Z" }, +] [[package]] name = "dotmap" @@ -586,29 +614,30 @@ wheels = [ [[package]] name = "faker" -version = "38.2.0" +version = "40.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "tzdata" }, + { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/27/022d4dbd4c20567b4c294f79a133cc2f05240ea61e0d515ead18c995c249/faker-38.2.0.tar.gz", hash = "sha256:20672803db9c7cb97f9b56c18c54b915b6f1d8991f63d1d673642dc43f5ce7ab", size = 1941469, upload-time = "2025-11-19T16:37:31.892Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/7e/dccb7013c9f3d66f2e379383600629fec75e4da2698548bdbf2041ea4b51/faker-40.4.0.tar.gz", hash = "sha256:76f8e74a3df28c3e2ec2caafa956e19e37a132fdc7ea067bc41783affcfee364", size = 1952221, upload-time = "2026-02-06T23:30:15.515Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/93/00c94d45f55c336434a15f98d906387e87ce28f9918e4444829a8fda432d/faker-38.2.0-py3-none-any.whl", hash = "sha256:35fe4a0a79dee0dc4103a6083ee9224941e7d3594811a50e3969e547b0d2ee65", size = 1980505, upload-time = "2025-11-19T16:37:30.208Z" }, + { url = "https://files.pythonhosted.org/packages/ac/63/58efa67c10fb27810d34351b7a10f85f109a7f7e2a07dc3773952459c47b/faker-40.4.0-py3-none-any.whl", hash = "sha256:486d43c67ebbb136bc932406418744f9a0bdf2c07f77703ea78b58b77e9aa443", size = 1987060, upload-time = "2026-02-06T23:30:13.44Z" }, ] [[package]] name = "fastapi" -version = "0.124.0" +version = "0.128.8" 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/48/9c/11969bd3e3bc4aa3a711f83dd3720239d3565a934929c74fc32f6c9f3638/fastapi-0.124.0.tar.gz", hash = "sha256:260cd178ad75e6d259991f2fd9b0fee924b224850079df576a3ba604ce58f4e6", size = 357623, upload-time = "2025-12-06T13:11:35.692Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/72/0df5c58c954742f31a7054e2dd1143bae0b408b7f36b59b85f928f9b456c/fastapi-0.128.8.tar.gz", hash = "sha256:3171f9f328c4a218f0a8d2ba8310ac3a55d1ee12c28c949650288aee25966007", size = 375523, upload-time = "2026-02-11T15:19:36.69Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/29/9e1e82e16e9a1763d3b55bfbe9b2fa39d7175a1fd97685c482fa402e111d/fastapi-0.124.0-py3-none-any.whl", hash = "sha256:91596bdc6dde303c318f06e8d2bc75eafb341fc793a0c9c92c0bc1db1ac52480", size = 112505, upload-time = "2025-12-06T13:11:34.392Z" }, + { url = "https://files.pythonhosted.org/packages/9f/37/37b07e276f8923c69a5df266bfcb5bac4ba8b55dfe4a126720f8c48681d1/fastapi-0.128.8-py3-none-any.whl", hash = "sha256:5618f492d0fe973a778f8fec97723f598aa9deee495040a8d51aaf3cf123ecf1", size = 103630, upload-time = "2026-02-11T15:19:35.209Z" }, ] [package.optional-dependencies] @@ -617,22 +646,24 @@ standard = [ { name = "fastapi-cli", extra = ["standard"] }, { name = "httpx" }, { name = "jinja2" }, + { name = "pydantic-extra-types" }, + { name = "pydantic-settings" }, { name = "python-multipart" }, { name = "uvicorn", extra = ["standard"] }, ] [[package]] name = "fastapi-cli" -version = "0.0.16" +version = "0.0.21" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "rich-toolkit" }, { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/75/9407a6b452be4c988feacec9c9d2f58d8f315162a6c7258d5a649d933ebe/fastapi_cli-0.0.16.tar.gz", hash = "sha256:e8a2a1ecf7a4e062e3b2eec63ae34387d1e142d4849181d936b23c4bdfe29073", size = 19447, upload-time = "2025-11-10T19:01:07.856Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/5a/500ec4deaa9a5d6bc7909cbd7b252fa37fe80d418c55a65ce5ed11c53505/fastapi_cli-0.0.21.tar.gz", hash = "sha256:457134b8f3e08d2d203a18db923a18bbc1a01d9de36fbe1fa7905c4d02a0e5c0", size = 19664, upload-time = "2026-02-11T15:27:59.65Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/43/678528c19318394320ee43757648d5e0a8070cf391b31f69d931e5c840d2/fastapi_cli-0.0.16-py3-none-any.whl", hash = "sha256:addcb6d130b5b9c91adbbf3f2947fe115991495fdb442fe3e51b5fc6327df9f4", size = 12312, upload-time = "2025-11-10T19:01:06.728Z" }, + { url = "https://files.pythonhosted.org/packages/de/cf/d1f3ea2a1661d80c62c7b1537184ec28ec832eefb7ad1ff3047813d19452/fastapi_cli-0.0.21-py3-none-any.whl", hash = "sha256:57c6e043694c68618eee04d00b4d93213c37f5a854b369d2871a77dfeff57e91", size = 12391, upload-time = "2026-02-11T15:27:58.181Z" }, ] [package.optional-dependencies] @@ -643,7 +674,7 @@ standard = [ [[package]] name = "fastapi-cloud-cli" -version = "0.6.0" +version = "0.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastar" }, @@ -655,9 +686,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/dd/e5890bb4ee63f9d8988660b755490e346cf5769aaa7f5f3ced9afb9f090a/fastapi_cloud_cli-0.6.0.tar.gz", hash = "sha256:2c333fff2e4b93b9efbec7896ce3ffa3e77ce4cf3d8cb14e35b4f823dfddac02", size = 30579, upload-time = "2025-12-04T15:04:07.008Z" } +sdist = { url = "https://files.pythonhosted.org/packages/11/15/6c3d85d63964340fde6f36cc80f3f365d35f371e6a918d68ff3a3d588ef2/fastapi_cloud_cli-0.11.0.tar.gz", hash = "sha256:ecc83a5db106be35af528eccb01aa9bced1d29783efd48c8c1c831cf111eea99", size = 36170, upload-time = "2026-01-15T09:51:33.681Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/2f/5ba9b5faa75067e30ff48e3c454263ebc2d2301d5509cfefe12cf9fc8156/fastapi_cloud_cli-0.6.0-py3-none-any.whl", hash = "sha256:b654890b5302c90d2f347b123a35186096328838a526316c470b6005cabd4983", size = 23215, upload-time = "2025-12-04T15:04:08.121Z" }, + { url = "https://files.pythonhosted.org/packages/1a/07/60f79270a3320780be7e2ae8a1740cb98a692920b569ba420b97bcc6e175/fastapi_cloud_cli-0.11.0-py3-none-any.whl", hash = "sha256:76857b0f09d918acfcb50ade34682ba3b2079ca0c43fda10215de301f185a7f8", size = 26884, upload-time = "2026-01-15T09:51:34.471Z" }, ] [[package]] @@ -692,16 +723,16 @@ dependencies = [ [[package]] name = "fastapi-pagination" -version = "0.15.2" +version = "0.15.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastapi" }, { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6a/8e/3f9b6980123a7003ca0fb0696f20a4308380f704603a600e6f0425f778c4/fastapi_pagination-0.15.2.tar.gz", hash = "sha256:e804014e853773a7c036b2e97379e79aab6715b8cb883d8ae251697d97b25223", size = 571283, upload-time = "2025-12-06T13:54:20.002Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/36/4314836683bec1b33195bbaf2d74e1515cfcbb7e7ef5431ef515b864a5d0/fastapi_pagination-0.15.10.tar.gz", hash = "sha256:0ba7d4f795059a91a9e89358af129f2114876452c1defaf198ea8e3419e9a3cd", size = 575160, upload-time = "2026-02-08T13:13:40.312Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/db/9430508035b18d905e898deb91be21afbad9c8016b59e29d13879e8100c8/fastapi_pagination-0.15.2-py3-none-any.whl", hash = "sha256:f8763a2e1caafe9c2a10ac860d6403935c31a067ffd6e93e57811bfec9873d87", size = 56106, upload-time = "2025-12-06T13:54:18.643Z" }, + { url = "https://files.pythonhosted.org/packages/91/95/cce73569317fdba138c315b980c39c6a035baa0ea5867d12276f1d312cff/fastapi_pagination-0.15.10-py3-none-any.whl", hash = "sha256:d50071ebc93b519391f16ff6c3ba9e3603bd659963fe6774ba2f4d5037e17fd8", size = 60798, upload-time = "2026-02-08T13:13:41.972Z" }, ] [[package]] @@ -718,7 +749,7 @@ wheels = [ [[package]] name = "fastapi-users" -version = "15.0.1" +version = "15.0.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "email-validator" }, @@ -728,9 +759,9 @@ dependencies = [ { name = "pyjwt", extra = ["crypto"] }, { name = "python-multipart" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/ea/6c0ba809f29d22ad53ab25bbae4408f00b0a3375b71bd21c39dcc3a16044/fastapi_users-15.0.1.tar.gz", hash = "sha256:c822755c1288740a919636d3463797e54df91b53c1c6f4917693d499867d21a7", size = 120916, upload-time = "2025-10-25T06:52:45.735Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/52/fadeae2c8435fb457a9cd91e402639fa5c9a25b16e6d204e043bf00cd875/fastapi_users-15.0.4.tar.gz", hash = "sha256:62657a4323de929cd98697b0fbdea77773ef271a6b57ef359080b9f773ebe144", size = 121394, upload-time = "2026-02-05T09:36:41.194Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/7f/1bff91a48e755e659d0505f597a8e010ec92059f2219a838fd15887a89b2/fastapi_users-15.0.1-py3-none-any.whl", hash = "sha256:6f637eb2fc80be6bba396b77dded30fe4c22fa943349d2e0a1647894f8b21c16", size = 38624, upload-time = "2025-10-25T06:52:44.119Z" }, + { url = "https://files.pythonhosted.org/packages/71/48/5fb2a18227ccbd5138515f21fc4fa8abcd9982238de43511d7f941e708db/fastapi_users-15.0.4-py3-none-any.whl", hash = "sha256:30940894825e1dd7b86f6013e4bc75eccc25ae8ce5261d1b180f6411bb28aff4", size = 39037, upload-time = "2026-02-05T09:36:42.195Z" }, ] [package.optional-dependencies] @@ -819,16 +850,16 @@ wheels = [ [[package]] name = "filelock" -version = "3.20.0" +version = "3.20.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" }, + { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, ] [[package]] name = "google-api-core" -version = "2.28.1" +version = "2.29.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-auth" }, @@ -837,14 +868,14 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/da/83d7043169ac2c8c7469f0e375610d78ae2160134bf1b80634c482fa079c/google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8", size = 176759, upload-time = "2025-10-28T21:34:51.529Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/10/05572d33273292bac49c2d1785925f7bc3ff2fe50e3044cf1062c1dde32e/google_api_core-2.29.0.tar.gz", hash = "sha256:84181be0f8e6b04006df75ddfe728f24489f0af57c96a529ff7cf45bc28797f7", size = 177828, upload-time = "2026-01-08T22:21:39.269Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/d4/90197b416cb61cefd316964fd9e7bd8324bcbafabf40eef14a9f20b81974/google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c", size = 173706, upload-time = "2025-10-28T21:34:50.151Z" }, + { url = "https://files.pythonhosted.org/packages/77/b6/85c4d21067220b9a78cfb81f516f9725ea6befc1544ec9bd2c1acd97c324/google_api_core-2.29.0-py3-none-any.whl", hash = "sha256:d30bc60980daa36e314b5d5a3e5958b0200cb44ca8fa1be2b614e932b75a3ea9", size = 173906, upload-time = "2026-01-08T22:21:36.093Z" }, ] [[package]] name = "google-api-python-client" -version = "2.187.0" +version = "2.190.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, @@ -853,36 +884,36 @@ dependencies = [ { name = "httplib2" }, { name = "uritemplate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/83/60cdacf139d768dd7f0fcbe8d95b418299810068093fdf8228c6af89bb70/google_api_python_client-2.187.0.tar.gz", hash = "sha256:e98e8e8f49e1b5048c2f8276473d6485febc76c9c47892a8b4d1afa2c9ec8278", size = 14068154, upload-time = "2025-11-06T01:48:53.274Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/8d/4ab3e3516b93bb50ed7814738ea61d49cba3f72f4e331dc9518ae2731e92/google_api_python_client-2.190.0.tar.gz", hash = "sha256:5357f34552e3724d80d2604c8fa146766e0a9d6bb0afada886fafed9feafeef6", size = 14111143, upload-time = "2026-02-12T00:38:03.37Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/58/c1e716be1b055b504d80db2c8413f6c6a890a6ae218a65f178b63bc30356/google_api_python_client-2.187.0-py3-none-any.whl", hash = "sha256:d8d0f6d85d7d1d10bdab32e642312ed572bdc98919f72f831b44b9a9cebba32f", size = 14641434, upload-time = "2025-11-06T01:48:50.763Z" }, + { url = "https://files.pythonhosted.org/packages/07/ad/223d5f4b0b987669ffeb3eadd7e9f85ece633aa7fd3246f1e2f6238e1e05/google_api_python_client-2.190.0-py3-none-any.whl", hash = "sha256:d9b5266758f96c39b8c21d9bbfeb4e58c14dbfba3c931f7c5a8d7fdcd292dd57", size = 14682070, upload-time = "2026-02-12T00:38:00.974Z" }, ] [[package]] name = "google-auth" -version = "2.43.0" +version = "2.48.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cachetools" }, + { name = "cryptography" }, { name = "pyasn1-modules" }, { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/ef/66d14cf0e01b08d2d51ffc3c20410c4e134a1548fc246a6081eae585a4fe/google_auth-2.43.0.tar.gz", hash = "sha256:88228eee5fc21b62a1b5fe773ca15e67778cb07dc8363adcb4a8827b52d81483", size = 296359, upload-time = "2025-11-06T00:13:36.587Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/41/242044323fbd746615884b1c16639749e73665b718209946ebad7ba8a813/google_auth-2.48.0.tar.gz", hash = "sha256:4f7e706b0cd3208a3d940a19a822c37a476ddba5450156c3e6624a71f7c841ce", size = 326522, upload-time = "2026-01-26T19:22:47.157Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/d1/385110a9ae86d91cc14c5282c61fe9f4dc41c0b9f7d423c6ad77038c4448/google_auth-2.43.0-py2.py3-none-any.whl", hash = "sha256:af628ba6fa493f75c7e9dbe9373d148ca9f4399b5ea29976519e0a3848eddd16", size = 223114, upload-time = "2025-11-06T00:13:35.209Z" }, + { url = "https://files.pythonhosted.org/packages/83/1d/d6466de3a5249d35e832a52834115ca9d1d0de6abc22065f049707516d47/google_auth-2.48.0-py3-none-any.whl", hash = "sha256:2e2a537873d449434252a9632c28bfc268b0adb1e53f9fb62afc5333a975903f", size = 236499, upload-time = "2026-01-26T19:22:45.099Z" }, ] [[package]] name = "google-auth-httplib2" -version = "0.2.1" +version = "0.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-auth" }, { name = "httplib2" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e0/83/7ef576d1c7ccea214e7b001e69c006bc75e058a3a1f2ab810167204b698b/google_auth_httplib2-0.2.1.tar.gz", hash = "sha256:5ef03be3927423c87fb69607b42df23a444e434ddb2555b73b3679793187b7de", size = 11086, upload-time = "2025-10-30T21:13:16.569Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/ad/c1f2b1175096a8d04cf202ad5ea6065f108d26be6fc7215876bde4a7981d/google_auth_httplib2-0.3.0.tar.gz", hash = "sha256:177898a0175252480d5ed916aeea183c2df87c1f9c26705d74ae6b951c268b0b", size = 11134, upload-time = "2025-12-15T22:13:51.825Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/a7/ca23dd006255f70e2bc469d3f9f0c82ea455335bfd682ad4d677adc435de/google_auth_httplib2-0.2.1-py3-none-any.whl", hash = "sha256:1be94c611db91c01f9703e7f62b0a59bbd5587a95571c7b6fade510d648bc08b", size = 9525, upload-time = "2025-10-30T21:13:15.758Z" }, + { url = "https://files.pythonhosted.org/packages/99/d5/3c97526c8796d3caf5f4b3bed2b05e8a7102326f00a334e7a438237f3b22/google_auth_httplib2-0.3.0-py3-none-any.whl", hash = "sha256:426167e5df066e3f5a0fc7ea18768c08e7296046594ce4c8c409c2457dd1f776", size = 9529, upload-time = "2025-12-15T22:13:51.048Z" }, ] [[package]] @@ -899,33 +930,36 @@ wheels = [ [[package]] name = "greenlet" -version = "3.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/e5/40dbda2736893e3e53d25838e0f19a2b417dfc122b9989c91918db30b5d3/greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb", size = 190651, upload-time = "2025-12-04T14:49:44.05Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/2f/28592176381b9ab2cafa12829ba7b472d177f3acc35d8fbcf3673d966fff/greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:a1e41a81c7e2825822f4e068c48cb2196002362619e2d70b148f20a831c00739", size = 275140, upload-time = "2025-12-04T14:23:01.282Z" }, - { url = "https://files.pythonhosted.org/packages/2c/80/fbe937bf81e9fca98c981fe499e59a3f45df2a04da0baa5c2be0dca0d329/greenlet-3.3.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f515a47d02da4d30caaa85b69474cec77b7929b2e936ff7fb853d42f4bf8808", size = 599219, upload-time = "2025-12-04T14:50:08.309Z" }, - { url = "https://files.pythonhosted.org/packages/c2/ff/7c985128f0514271b8268476af89aee6866df5eec04ac17dcfbc676213df/greenlet-3.3.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d2d9fd66bfadf230b385fdc90426fcd6eb64db54b40c495b72ac0feb5766c54", size = 610211, upload-time = "2025-12-04T14:57:43.968Z" }, - { url = "https://files.pythonhosted.org/packages/79/07/c47a82d881319ec18a4510bb30463ed6891f2ad2c1901ed5ec23d3de351f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30a6e28487a790417d036088b3bcb3f3ac7d8babaa7d0139edbaddebf3af9492", size = 624311, upload-time = "2025-12-04T15:07:14.697Z" }, - { url = "https://files.pythonhosted.org/packages/fd/8e/424b8c6e78bd9837d14ff7df01a9829fc883ba2ab4ea787d4f848435f23f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:087ea5e004437321508a8d6f20efc4cfec5e3c30118e1417ea96ed1d93950527", size = 612833, upload-time = "2025-12-04T14:26:03.669Z" }, - { url = "https://files.pythonhosted.org/packages/b5/ba/56699ff9b7c76ca12f1cdc27a886d0f81f2189c3455ff9f65246780f713d/greenlet-3.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab97cf74045343f6c60a39913fa59710e4bd26a536ce7ab2397adf8b27e67c39", size = 1567256, upload-time = "2025-12-04T15:04:25.276Z" }, - { url = "https://files.pythonhosted.org/packages/1e/37/f31136132967982d698c71a281a8901daf1a8fbab935dce7c0cf15f942cc/greenlet-3.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5375d2e23184629112ca1ea89a53389dddbffcf417dad40125713d88eb5f96e8", size = 1636483, upload-time = "2025-12-04T14:27:30.804Z" }, - { url = "https://files.pythonhosted.org/packages/7e/71/ba21c3fb8c5dce83b8c01f458a42e99ffdb1963aeec08fff5a18588d8fd7/greenlet-3.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:9ee1942ea19550094033c35d25d20726e4f1c40d59545815e1128ac58d416d38", size = 301833, upload-time = "2025-12-04T14:32:23.929Z" }, - { url = "https://files.pythonhosted.org/packages/d7/7c/f0a6d0ede2c7bf092d00bc83ad5bafb7e6ec9b4aab2fbdfa6f134dc73327/greenlet-3.3.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:60c2ef0f578afb3c8d92ea07ad327f9a062547137afe91f38408f08aacab667f", size = 275671, upload-time = "2025-12-04T14:23:05.267Z" }, - { url = "https://files.pythonhosted.org/packages/44/06/dac639ae1a50f5969d82d2e3dd9767d30d6dbdbab0e1a54010c8fe90263c/greenlet-3.3.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5d554d0712ba1de0a6c94c640f7aeba3f85b3a6e1f2899c11c2c0428da9365", size = 646360, upload-time = "2025-12-04T14:50:10.026Z" }, - { url = "https://files.pythonhosted.org/packages/e0/94/0fb76fe6c5369fba9bf98529ada6f4c3a1adf19e406a47332245ef0eb357/greenlet-3.3.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3a898b1e9c5f7307ebbde4102908e6cbfcb9ea16284a3abe15cab996bee8b9b3", size = 658160, upload-time = "2025-12-04T14:57:45.41Z" }, - { url = "https://files.pythonhosted.org/packages/93/79/d2c70cae6e823fac36c3bbc9077962105052b7ef81db2f01ec3b9bf17e2b/greenlet-3.3.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dcd2bdbd444ff340e8d6bdf54d2f206ccddbb3ccfdcd3c25bf4afaa7b8f0cf45", size = 671388, upload-time = "2025-12-04T15:07:15.789Z" }, - { url = "https://files.pythonhosted.org/packages/b8/14/bab308fc2c1b5228c3224ec2bf928ce2e4d21d8046c161e44a2012b5203e/greenlet-3.3.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5773edda4dc00e173820722711d043799d3adb4f01731f40619e07ea2750b955", size = 660166, upload-time = "2025-12-04T14:26:05.099Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d2/91465d39164eaa0085177f61983d80ffe746c5a1860f009811d498e7259c/greenlet-3.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ac0549373982b36d5fd5d30beb8a7a33ee541ff98d2b502714a09f1169f31b55", size = 1615193, upload-time = "2025-12-04T15:04:27.041Z" }, - { url = "https://files.pythonhosted.org/packages/42/1b/83d110a37044b92423084d52d5d5a3b3a73cafb51b547e6d7366ff62eff1/greenlet-3.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d198d2d977460358c3b3a4dc844f875d1adb33817f0613f663a656f463764ccc", size = 1683653, upload-time = "2025-12-04T14:27:32.366Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/9030e6f9aa8fd7808e9c31ba4c38f87c4f8ec324ee67431d181fe396d705/greenlet-3.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:73f51dd0e0bdb596fb0417e475fa3c5e32d4c83638296e560086b8d7da7c4170", size = 305387, upload-time = "2025-12-04T14:26:51.063Z" }, - { url = "https://files.pythonhosted.org/packages/a0/66/bd6317bc5932accf351fc19f177ffba53712a202f9df10587da8df257c7e/greenlet-3.3.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d6ed6f85fae6cdfdb9ce04c9bf7a08d666cfcfb914e7d006f44f840b46741931", size = 282638, upload-time = "2025-12-04T14:25:20.941Z" }, - { url = "https://files.pythonhosted.org/packages/30/cf/cc81cb030b40e738d6e69502ccbd0dd1bced0588e958f9e757945de24404/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9125050fcf24554e69c4cacb086b87b3b55dc395a8b3ebe6487b045b2614388", size = 651145, upload-time = "2025-12-04T14:50:11.039Z" }, - { url = "https://files.pythonhosted.org/packages/9c/ea/1020037b5ecfe95ca7df8d8549959baceb8186031da83d5ecceff8b08cd2/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:87e63ccfa13c0a0f6234ed0add552af24cc67dd886731f2261e46e241608bee3", size = 654236, upload-time = "2025-12-04T14:57:47.007Z" }, - { url = "https://files.pythonhosted.org/packages/69/cc/1e4bae2e45ca2fa55299f4e85854606a78ecc37fead20d69322f96000504/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2662433acbca297c9153a4023fe2161c8dcfdcc91f10433171cf7e7d94ba2221", size = 662506, upload-time = "2025-12-04T15:07:16.906Z" }, - { url = "https://files.pythonhosted.org/packages/57/b9/f8025d71a6085c441a7eaff0fd928bbb275a6633773667023d19179fe815/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c6e9b9c1527a78520357de498b0e709fb9e2f49c3a513afd5a249007261911b", size = 653783, upload-time = "2025-12-04T14:26:06.225Z" }, - { url = "https://files.pythonhosted.org/packages/f6/c7/876a8c7a7485d5d6b5c6821201d542ef28be645aa024cfe1145b35c120c1/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:286d093f95ec98fdd92fcb955003b8a3d054b4e2cab3e2707a5039e7b50520fd", size = 1614857, upload-time = "2025-12-04T15:04:28.484Z" }, - { url = "https://files.pythonhosted.org/packages/4f/dc/041be1dff9f23dac5f48a43323cd0789cb798342011c19a248d9c9335536/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c10513330af5b8ae16f023e8ddbfb486ab355d04467c4679c5cfe4659975dd9", size = 1676034, upload-time = "2025-12-04T14:27:33.531Z" }, +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/99/1cd3411c56a410994669062bd73dd58270c00cc074cac15f385a1fd91f8a/greenlet-3.3.1.tar.gz", hash = "sha256:41848f3230b58c08bb43dee542e74a2a2e34d3c59dc3076cec9151aeeedcae98", size = 184690, upload-time = "2026-01-23T15:31:02.076Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/ab/d26750f2b7242c2b90ea2ad71de70cfcd73a948a49513188a0fc0d6fc15a/greenlet-3.3.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:7ab327905cabb0622adca5971e488064e35115430cec2c35a50fd36e72a315b3", size = 275205, upload-time = "2026-01-23T15:30:24.556Z" }, + { url = "https://files.pythonhosted.org/packages/10/d3/be7d19e8fad7c5a78eeefb2d896a08cd4643e1e90c605c4be3b46264998f/greenlet-3.3.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65be2f026ca6a176f88fb935ee23c18333ccea97048076aef4db1ef5bc0713ac", size = 599284, upload-time = "2026-01-23T16:00:58.584Z" }, + { url = "https://files.pythonhosted.org/packages/ae/21/fe703aaa056fdb0f17e5afd4b5c80195bbdab701208918938bd15b00d39b/greenlet-3.3.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7a3ae05b3d225b4155bda56b072ceb09d05e974bc74be6c3fc15463cf69f33fd", size = 610274, upload-time = "2026-01-23T16:05:29.312Z" }, + { url = "https://files.pythonhosted.org/packages/06/00/95df0b6a935103c0452dad2203f5be8377e551b8466a29650c4c5a5af6cc/greenlet-3.3.1-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:12184c61e5d64268a160226fb4818af4df02cfead8379d7f8b99a56c3a54ff3e", size = 624375, upload-time = "2026-01-23T16:15:55.915Z" }, + { url = "https://files.pythonhosted.org/packages/cb/86/5c6ab23bb3c28c21ed6bebad006515cfe08b04613eb105ca0041fecca852/greenlet-3.3.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6423481193bbbe871313de5fd06a082f2649e7ce6e08015d2a76c1e9186ca5b3", size = 612904, upload-time = "2026-01-23T15:32:52.317Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f3/7949994264e22639e40718c2daf6f6df5169bf48fb038c008a489ec53a50/greenlet-3.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:33a956fe78bbbda82bfc95e128d61129b32d66bcf0a20a1f0c08aa4839ffa951", size = 1567316, upload-time = "2026-01-23T16:04:23.316Z" }, + { url = "https://files.pythonhosted.org/packages/8d/6e/d73c94d13b6465e9f7cd6231c68abde838bb22408596c05d9059830b7872/greenlet-3.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b065d3284be43728dd280f6f9a13990b56470b81be20375a207cdc814a983f2", size = 1636549, upload-time = "2026-01-23T15:33:48.643Z" }, + { url = "https://files.pythonhosted.org/packages/5e/b3/c9c23a6478b3bcc91f979ce4ca50879e4d0b2bd7b9a53d8ecded719b92e2/greenlet-3.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:27289986f4e5b0edec7b5a91063c109f0276abb09a7e9bdab08437525977c946", size = 227042, upload-time = "2026-01-23T15:33:58.216Z" }, + { url = "https://files.pythonhosted.org/packages/90/e7/824beda656097edee36ab15809fd063447b200cc03a7f6a24c34d520bc88/greenlet-3.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:2f080e028001c5273e0b42690eaf359aeef9cb1389da0f171ea51a5dc3c7608d", size = 226294, upload-time = "2026-01-23T15:30:52.73Z" }, + { url = "https://files.pythonhosted.org/packages/ae/fb/011c7c717213182caf78084a9bea51c8590b0afda98001f69d9f853a495b/greenlet-3.3.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:bd59acd8529b372775cd0fcbc5f420ae20681c5b045ce25bd453ed8455ab99b5", size = 275737, upload-time = "2026-01-23T15:32:16.889Z" }, + { url = "https://files.pythonhosted.org/packages/41/2e/a3a417d620363fdbb08a48b1dd582956a46a61bf8fd27ee8164f9dfe87c2/greenlet-3.3.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b31c05dd84ef6871dd47120386aed35323c944d86c3d91a17c4b8d23df62f15b", size = 646422, upload-time = "2026-01-23T16:01:00.354Z" }, + { url = "https://files.pythonhosted.org/packages/b4/09/c6c4a0db47defafd2d6bab8ddfe47ad19963b4e30f5bed84d75328059f8c/greenlet-3.3.1-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:02925a0bfffc41e542c70aa14c7eda3593e4d7e274bfcccca1827e6c0875902e", size = 658219, upload-time = "2026-01-23T16:05:30.956Z" }, + { url = "https://files.pythonhosted.org/packages/e2/89/b95f2ddcc5f3c2bc09c8ee8d77be312df7f9e7175703ab780f2014a0e781/greenlet-3.3.1-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3e0f3878ca3a3ff63ab4ea478585942b53df66ddde327b59ecb191b19dbbd62d", size = 671455, upload-time = "2026-01-23T16:15:57.232Z" }, + { url = "https://files.pythonhosted.org/packages/80/38/9d42d60dffb04b45f03dbab9430898352dba277758640751dc5cc316c521/greenlet-3.3.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34a729e2e4e4ffe9ae2408d5ecaf12f944853f40ad724929b7585bca808a9d6f", size = 660237, upload-time = "2026-01-23T15:32:53.967Z" }, + { url = "https://files.pythonhosted.org/packages/96/61/373c30b7197f9e756e4c81ae90a8d55dc3598c17673f91f4d31c3c689c3f/greenlet-3.3.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:aec9ab04e82918e623415947921dea15851b152b822661cce3f8e4393c3df683", size = 1615261, upload-time = "2026-01-23T16:04:25.066Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d3/ca534310343f5945316f9451e953dcd89b36fe7a19de652a1dc5a0eeef3f/greenlet-3.3.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:71c767cf281a80d02b6c1bdc41c9468e1f5a494fb11bc8688c360524e273d7b1", size = 1683719, upload-time = "2026-01-23T15:33:50.61Z" }, + { url = "https://files.pythonhosted.org/packages/52/cb/c21a3fd5d2c9c8b622e7bede6d6d00e00551a5ee474ea6d831b5f567a8b4/greenlet-3.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:96aff77af063b607f2489473484e39a0bbae730f2ea90c9e5606c9b73c44174a", size = 228125, upload-time = "2026-01-23T15:32:45.265Z" }, + { url = "https://files.pythonhosted.org/packages/6a/8e/8a2db6d11491837af1de64b8aff23707c6e85241be13c60ed399a72e2ef8/greenlet-3.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:b066e8b50e28b503f604fa538adc764a638b38cf8e81e025011d26e8a627fa79", size = 227519, upload-time = "2026-01-23T15:31:47.284Z" }, + { url = "https://files.pythonhosted.org/packages/28/24/cbbec49bacdcc9ec652a81d3efef7b59f326697e7edf6ed775a5e08e54c2/greenlet-3.3.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:3e63252943c921b90abb035ebe9de832c436401d9c45f262d80e2d06cc659242", size = 282706, upload-time = "2026-01-23T15:33:05.525Z" }, + { url = "https://files.pythonhosted.org/packages/86/2e/4f2b9323c144c4fe8842a4e0d92121465485c3c2c5b9e9b30a52e80f523f/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76e39058e68eb125de10c92524573924e827927df5d3891fbc97bd55764a8774", size = 651209, upload-time = "2026-01-23T16:01:01.517Z" }, + { url = "https://files.pythonhosted.org/packages/d9/87/50ca60e515f5bb55a2fbc5f0c9b5b156de7d2fc51a0a69abc9d23914a237/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9f9d5e7a9310b7a2f416dd13d2e3fd8b42d803968ea580b7c0f322ccb389b97", size = 654300, upload-time = "2026-01-23T16:05:32.199Z" }, + { url = "https://files.pythonhosted.org/packages/7c/25/c51a63f3f463171e09cb586eb64db0861eb06667ab01a7968371a24c4f3b/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b9721549a95db96689458a1e0ae32412ca18776ed004463df3a9299c1b257ab", size = 662574, upload-time = "2026-01-23T16:15:58.364Z" }, + { url = "https://files.pythonhosted.org/packages/1d/94/74310866dfa2b73dd08659a3d18762f83985ad3281901ba0ee9a815194fb/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92497c78adf3ac703b57f1e3813c2d874f27f71a178f9ea5887855da413cd6d2", size = 653842, upload-time = "2026-01-23T15:32:55.671Z" }, + { url = "https://files.pythonhosted.org/packages/97/43/8bf0ffa3d498eeee4c58c212a3905dd6146c01c8dc0b0a046481ca29b18c/greenlet-3.3.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ed6b402bc74d6557a705e197d47f9063733091ed6357b3de33619d8a8d93ac53", size = 1614917, upload-time = "2026-01-23T16:04:26.276Z" }, + { url = "https://files.pythonhosted.org/packages/89/90/a3be7a5f378fc6e84abe4dcfb2ba32b07786861172e502388b4c90000d1b/greenlet-3.3.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:59913f1e5ada20fde795ba906916aea25d442abcc0593fba7e26c92b7ad76249", size = 1676092, upload-time = "2026-01-23T15:33:52.176Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2b/98c7f93e6db9977aaee07eb1e51ca63bd5f779b900d362791d3252e60558/greenlet-3.3.1-cp314-cp314t-win_amd64.whl", hash = "sha256:301860987846c24cb8964bdec0e31a96ad4a2a801b41b4ef40963c1b44f33451", size = 233181, upload-time = "2026-01-23T15:33:00.29Z" }, ] [[package]] @@ -952,14 +986,14 @@ wheels = [ [[package]] name = "httplib2" -version = "0.31.0" +version = "0.31.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyparsing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/77/6653db69c1f7ecfe5e3f9726fdadc981794656fcd7d98c4209fecfea9993/httplib2-0.31.0.tar.gz", hash = "sha256:ac7ab497c50975147d4f7b1ade44becc7df2f8954d42b38b3d69c515f531135c", size = 250759, upload-time = "2025-09-11T12:16:03.403Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/1f/e86365613582c027dda5ddb64e1010e57a3d53e99ab8a72093fa13d565ec/httplib2-0.31.2.tar.gz", hash = "sha256:385e0869d7397484f4eab426197a4c020b606edd43372492337c0b4010ae5d24", size = 250800, upload-time = "2026-01-23T11:04:44.165Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/a2/0d269db0f6163be503775dc8b6a6fa15820cc9fdc866f6ba608d86b721f2/httplib2-0.31.0-py3-none-any.whl", hash = "sha256:b9cd78abea9b4e43a7714c6e0f8b6b8561a6fc1e95d5dbd367f5bf0ef35f5d24", size = 91148, upload-time = "2025-09-11T12:16:01.803Z" }, + { url = "https://files.pythonhosted.org/packages/2f/90/fd509079dfcab01102c0fdd87f3a9506894bc70afcf9e9785ef6b2b3aff6/httplib2-0.31.2-py3-none-any.whl", hash = "sha256:dbf0c2fa3862acf3c55c078ea9c0bc4481d7dc5117cae71be9514912cf9f8349", size = 91099, upload-time = "2026-01-23T11:04:42.78Z" }, ] [[package]] @@ -1064,11 +1098,11 @@ wheels = [ [[package]] name = "jmespath" -version = "1.0.1" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843, upload-time = "2022-06-17T18:00:12.224Z" } +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/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" }, + { 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]] @@ -1094,11 +1128,11 @@ wheels = [ [[package]] name = "markdown" -version = "3.10" +version = "3.10.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/7dd27d9d863b3376fcf23a5a13cb5d024aed1db46f963f1b5735ae43b3be/markdown-3.10.tar.gz", hash = "sha256:37062d4f2aa4b2b6b32aefb80faa300f82cc790cb949a35b8caede34f2b68c0e", size = 364931, upload-time = "2025-11-03T19:51:15.007Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl", hash = "sha256:b5b99d6951e2e4948d939255596523444c0e677c669700b1d17aa4a8a464cb7c", size = 107678, upload-time = "2025-11-03T19:51:13.887Z" }, + { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, ] [[package]] @@ -1176,78 +1210,68 @@ wheels = [ [[package]] name = "mjml" -version = "0.11.1" +version = "0.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beautifulsoup4" }, - { name = "docopt" }, + { name = "docopt-ng" }, { name = "dotmap" }, { name = "jinja2" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/68/4e0e1b0bc64f0d3afac2fb8a4fb35f2a4e9a0521ae1c777c0e29e21b27fa/mjml-0.11.1.tar.gz", hash = "sha256:f703c8b3458ca0100df6cf56a3633f193b352a80b1a1836a452b92361e74ca73", size = 66589, upload-time = "2025-05-13T10:24:05.693Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/85/a6/7ed27888adbf8cbdd734e298691004918ec0ef5f40e6bc1329ed97da2273/mjml-0.11.1-py3-none-any.whl", hash = "sha256:fef9f7a95929cbe5ddce9351ee8702e05153d68abc77dcf8e84da2c22a330b2a", size = 63191, upload-time = "2025-05-13T10:24:03.953Z" }, -] - -[[package]] -name = "nodeenv" -version = "1.9.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/e3/ab5a6fedd48eb2122030e9b4eb6fa560696eba6ec7a652dc92bdca22459d/mjml-0.12.0.tar.gz", hash = "sha256:614397e624b115d78f9064d0f8aabceda3b522785eca8c2818ee03ffa8fdbf37", size = 72684, upload-time = "2025-12-27T19:32:42.584Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, + { url = "https://files.pythonhosted.org/packages/a2/34/389deb78f9a4f86945532572f417ea5ec68227cc4f67d3140998cfaceafb/mjml-0.12.0-py3-none-any.whl", hash = "sha256:2329aa6b31237ce7309c5605e049d5c110a841218220f6af80bd9731040947da", size = 66884, upload-time = "2025-12-27T19:32:41.344Z" }, ] [[package]] name = "numpy" -version = "2.3.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/76/65/21b3bc86aac7b8f2862db1e808f1ea22b028e30a225a34a5ede9bf8678f2/numpy-2.3.5.tar.gz", hash = "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", size = 20584950, upload-time = "2025-11-16T22:52:42.067Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/69/9cde09f36da4b5a505341180a3f2e6fadc352fd4d2b7096ce9778db83f1a/numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff", size = 16728251, upload-time = "2025-11-16T22:50:19.013Z" }, - { url = "https://files.pythonhosted.org/packages/79/fb/f505c95ceddd7027347b067689db71ca80bd5ecc926f913f1a23e65cf09b/numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188", size = 12254652, upload-time = "2025-11-16T22:50:21.487Z" }, - { url = "https://files.pythonhosted.org/packages/78/da/8c7738060ca9c31b30e9301ee0cf6c5ffdbf889d9593285a1cead337f9a5/numpy-2.3.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0", size = 5083172, upload-time = "2025-11-16T22:50:24.562Z" }, - { url = "https://files.pythonhosted.org/packages/a4/b4/ee5bb2537fb9430fd2ef30a616c3672b991a4129bb1c7dcc42aa0abbe5d7/numpy-2.3.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903", size = 6622990, upload-time = "2025-11-16T22:50:26.47Z" }, - { url = "https://files.pythonhosted.org/packages/95/03/dc0723a013c7d7c19de5ef29e932c3081df1c14ba582b8b86b5de9db7f0f/numpy-2.3.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d", size = 14248902, upload-time = "2025-11-16T22:50:28.861Z" }, - { url = "https://files.pythonhosted.org/packages/f5/10/ca162f45a102738958dcec8023062dad0cbc17d1ab99d68c4e4a6c45fb2b/numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017", size = 16597430, upload-time = "2025-11-16T22:50:31.56Z" }, - { url = "https://files.pythonhosted.org/packages/2a/51/c1e29be863588db58175175f057286900b4b3327a1351e706d5e0f8dd679/numpy-2.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf", size = 16024551, upload-time = "2025-11-16T22:50:34.242Z" }, - { url = "https://files.pythonhosted.org/packages/83/68/8236589d4dbb87253d28259d04d9b814ec0ecce7cb1c7fed29729f4c3a78/numpy-2.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce", size = 18533275, upload-time = "2025-11-16T22:50:37.651Z" }, - { url = "https://files.pythonhosted.org/packages/40/56/2932d75b6f13465239e3b7b7e511be27f1b8161ca2510854f0b6e521c395/numpy-2.3.5-cp313-cp313-win32.whl", hash = "sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e", size = 6277637, upload-time = "2025-11-16T22:50:40.11Z" }, - { url = "https://files.pythonhosted.org/packages/0c/88/e2eaa6cffb115b85ed7c7c87775cb8bcf0816816bc98ca8dbfa2ee33fe6e/numpy-2.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b", size = 12779090, upload-time = "2025-11-16T22:50:42.503Z" }, - { url = "https://files.pythonhosted.org/packages/8f/88/3f41e13a44ebd4034ee17baa384acac29ba6a4fcc2aca95f6f08ca0447d1/numpy-2.3.5-cp313-cp313-win_arm64.whl", hash = "sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae", size = 10194710, upload-time = "2025-11-16T22:50:44.971Z" }, - { url = "https://files.pythonhosted.org/packages/13/cb/71744144e13389d577f867f745b7df2d8489463654a918eea2eeb166dfc9/numpy-2.3.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd", size = 16827292, upload-time = "2025-11-16T22:50:47.715Z" }, - { url = "https://files.pythonhosted.org/packages/71/80/ba9dc6f2a4398e7f42b708a7fdc841bb638d353be255655498edbf9a15a8/numpy-2.3.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f", size = 12378897, upload-time = "2025-11-16T22:50:51.327Z" }, - { url = "https://files.pythonhosted.org/packages/2e/6d/db2151b9f64264bcceccd51741aa39b50150de9b602d98ecfe7e0c4bff39/numpy-2.3.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a", size = 5207391, upload-time = "2025-11-16T22:50:54.542Z" }, - { url = "https://files.pythonhosted.org/packages/80/ae/429bacace5ccad48a14c4ae5332f6aa8ab9f69524193511d60ccdfdc65fa/numpy-2.3.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139", size = 6721275, upload-time = "2025-11-16T22:50:56.794Z" }, - { url = "https://files.pythonhosted.org/packages/74/5b/1919abf32d8722646a38cd527bc3771eb229a32724ee6ba340ead9b92249/numpy-2.3.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e", size = 14306855, upload-time = "2025-11-16T22:50:59.208Z" }, - { url = "https://files.pythonhosted.org/packages/a5/87/6831980559434973bebc30cd9c1f21e541a0f2b0c280d43d3afd909b66d0/numpy-2.3.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9", size = 16657359, upload-time = "2025-11-16T22:51:01.991Z" }, - { url = "https://files.pythonhosted.org/packages/dd/91/c797f544491ee99fd00495f12ebb7802c440c1915811d72ac5b4479a3356/numpy-2.3.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946", size = 16093374, upload-time = "2025-11-16T22:51:05.291Z" }, - { url = "https://files.pythonhosted.org/packages/74/a6/54da03253afcbe7a72785ec4da9c69fb7a17710141ff9ac5fcb2e32dbe64/numpy-2.3.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1", size = 18594587, upload-time = "2025-11-16T22:51:08.585Z" }, - { url = "https://files.pythonhosted.org/packages/80/e9/aff53abbdd41b0ecca94285f325aff42357c6b5abc482a3fcb4994290b18/numpy-2.3.5-cp313-cp313t-win32.whl", hash = "sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3", size = 6405940, upload-time = "2025-11-16T22:51:11.541Z" }, - { url = "https://files.pythonhosted.org/packages/d5/81/50613fec9d4de5480de18d4f8ef59ad7e344d497edbef3cfd80f24f98461/numpy-2.3.5-cp313-cp313t-win_amd64.whl", hash = "sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234", size = 12920341, upload-time = "2025-11-16T22:51:14.312Z" }, - { url = "https://files.pythonhosted.org/packages/bb/ab/08fd63b9a74303947f34f0bd7c5903b9c5532c2d287bead5bdf4c556c486/numpy-2.3.5-cp313-cp313t-win_arm64.whl", hash = "sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7", size = 10262507, upload-time = "2025-11-16T22:51:16.846Z" }, - { url = "https://files.pythonhosted.org/packages/ba/97/1a914559c19e32d6b2e233cf9a6a114e67c856d35b1d6babca571a3e880f/numpy-2.3.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82", size = 16735706, upload-time = "2025-11-16T22:51:19.558Z" }, - { url = "https://files.pythonhosted.org/packages/57/d4/51233b1c1b13ecd796311216ae417796b88b0616cfd8a33ae4536330748a/numpy-2.3.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0", size = 12264507, upload-time = "2025-11-16T22:51:22.492Z" }, - { url = "https://files.pythonhosted.org/packages/45/98/2fe46c5c2675b8306d0b4a3ec3494273e93e1226a490f766e84298576956/numpy-2.3.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63", size = 5093049, upload-time = "2025-11-16T22:51:25.171Z" }, - { url = "https://files.pythonhosted.org/packages/ce/0e/0698378989bb0ac5f1660c81c78ab1fe5476c1a521ca9ee9d0710ce54099/numpy-2.3.5-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9", size = 6626603, upload-time = "2025-11-16T22:51:27Z" }, - { url = "https://files.pythonhosted.org/packages/5e/a6/9ca0eecc489640615642a6cbc0ca9e10df70df38c4d43f5a928ff18d8827/numpy-2.3.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b", size = 14262696, upload-time = "2025-11-16T22:51:29.402Z" }, - { url = "https://files.pythonhosted.org/packages/c8/f6/07ec185b90ec9d7217a00eeeed7383b73d7e709dae2a9a021b051542a708/numpy-2.3.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520", size = 16597350, upload-time = "2025-11-16T22:51:32.167Z" }, - { url = "https://files.pythonhosted.org/packages/75/37/164071d1dde6a1a84c9b8e5b414fa127981bad47adf3a6b7e23917e52190/numpy-2.3.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c", size = 16040190, upload-time = "2025-11-16T22:51:35.403Z" }, - { url = "https://files.pythonhosted.org/packages/08/3c/f18b82a406b04859eb026d204e4e1773eb41c5be58410f41ffa511d114ae/numpy-2.3.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8", size = 18536749, upload-time = "2025-11-16T22:51:39.698Z" }, - { url = "https://files.pythonhosted.org/packages/40/79/f82f572bf44cf0023a2fe8588768e23e1592585020d638999f15158609e1/numpy-2.3.5-cp314-cp314-win32.whl", hash = "sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248", size = 6335432, upload-time = "2025-11-16T22:51:42.476Z" }, - { url = "https://files.pythonhosted.org/packages/a3/2e/235b4d96619931192c91660805e5e49242389742a7a82c27665021db690c/numpy-2.3.5-cp314-cp314-win_amd64.whl", hash = "sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e", size = 12919388, upload-time = "2025-11-16T22:51:45.275Z" }, - { url = "https://files.pythonhosted.org/packages/07/2b/29fd75ce45d22a39c61aad74f3d718e7ab67ccf839ca8b60866054eb15f8/numpy-2.3.5-cp314-cp314-win_arm64.whl", hash = "sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2", size = 10476651, upload-time = "2025-11-16T22:51:47.749Z" }, - { url = "https://files.pythonhosted.org/packages/17/e1/f6a721234ebd4d87084cfa68d081bcba2f5cfe1974f7de4e0e8b9b2a2ba1/numpy-2.3.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41", size = 16834503, upload-time = "2025-11-16T22:51:50.443Z" }, - { url = "https://files.pythonhosted.org/packages/5c/1c/baf7ffdc3af9c356e1c135e57ab7cf8d247931b9554f55c467efe2c69eff/numpy-2.3.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad", size = 12381612, upload-time = "2025-11-16T22:51:53.609Z" }, - { url = "https://files.pythonhosted.org/packages/74/91/f7f0295151407ddc9ba34e699013c32c3c91944f9b35fcf9281163dc1468/numpy-2.3.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39", size = 5210042, upload-time = "2025-11-16T22:51:56.213Z" }, - { url = "https://files.pythonhosted.org/packages/2e/3b/78aebf345104ec50dd50a4d06ddeb46a9ff5261c33bcc58b1c4f12f85ec2/numpy-2.3.5-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20", size = 6724502, upload-time = "2025-11-16T22:51:58.584Z" }, - { url = "https://files.pythonhosted.org/packages/02/c6/7c34b528740512e57ef1b7c8337ab0b4f0bddf34c723b8996c675bc2bc91/numpy-2.3.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52", size = 14308962, upload-time = "2025-11-16T22:52:01.698Z" }, - { url = "https://files.pythonhosted.org/packages/80/35/09d433c5262bc32d725bafc619e095b6a6651caf94027a03da624146f655/numpy-2.3.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b", size = 16655054, upload-time = "2025-11-16T22:52:04.267Z" }, - { url = "https://files.pythonhosted.org/packages/7a/ab/6a7b259703c09a88804fa2430b43d6457b692378f6b74b356155283566ac/numpy-2.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3", size = 16091613, upload-time = "2025-11-16T22:52:08.651Z" }, - { url = "https://files.pythonhosted.org/packages/c2/88/330da2071e8771e60d1038166ff9d73f29da37b01ec3eb43cb1427464e10/numpy-2.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227", size = 18591147, upload-time = "2025-11-16T22:52:11.453Z" }, - { url = "https://files.pythonhosted.org/packages/51/41/851c4b4082402d9ea860c3626db5d5df47164a712cb23b54be028b184c1c/numpy-2.3.5-cp314-cp314t-win32.whl", hash = "sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5", size = 6479806, upload-time = "2025-11-16T22:52:14.641Z" }, - { url = "https://files.pythonhosted.org/packages/90/30/d48bde1dfd93332fa557cff1972fbc039e055a52021fbef4c2c4b1eefd17/numpy-2.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf", size = 13105760, upload-time = "2025-11-16T22:52:17.975Z" }, - { url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459, upload-time = "2025-11-16T22:52:20.55Z" }, +version = "2.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/22/815b9fe25d1d7ae7d492152adbc7226d3eff731dffc38fe970589fcaaa38/numpy-2.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c", size = 16663696, upload-time = "2026-01-31T23:11:17.516Z" }, + { url = "https://files.pythonhosted.org/packages/09/f0/817d03a03f93ba9c6c8993de509277d84e69f9453601915e4a69554102a1/numpy-2.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979", size = 14688322, upload-time = "2026-01-31T23:11:19.883Z" }, + { url = "https://files.pythonhosted.org/packages/da/b4/f805ab79293c728b9a99438775ce51885fd4f31b76178767cfc718701a39/numpy-2.4.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98", size = 5198157, upload-time = "2026-01-31T23:11:22.375Z" }, + { url = "https://files.pythonhosted.org/packages/74/09/826e4289844eccdcd64aac27d13b0fd3f32039915dd5b9ba01baae1f436c/numpy-2.4.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef", size = 6546330, upload-time = "2026-01-31T23:11:23.958Z" }, + { url = "https://files.pythonhosted.org/packages/19/fb/cbfdbfa3057a10aea5422c558ac57538e6acc87ec1669e666d32ac198da7/numpy-2.4.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7", size = 15660968, upload-time = "2026-01-31T23:11:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/04/dc/46066ce18d01645541f0186877377b9371b8fa8017fa8262002b4ef22612/numpy-2.4.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499", size = 16607311, upload-time = "2026-01-31T23:11:28.117Z" }, + { url = "https://files.pythonhosted.org/packages/14/d9/4b5adfc39a43fa6bf918c6d544bc60c05236cc2f6339847fc5b35e6cb5b0/numpy-2.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb", size = 17012850, upload-time = "2026-01-31T23:11:30.888Z" }, + { url = "https://files.pythonhosted.org/packages/b7/20/adb6e6adde6d0130046e6fdfb7675cc62bc2f6b7b02239a09eb58435753d/numpy-2.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7", size = 18334210, upload-time = "2026-01-31T23:11:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/78/0e/0a73b3dff26803a8c02baa76398015ea2a5434d9b8265a7898a6028c1591/numpy-2.4.2-cp313-cp313-win32.whl", hash = "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110", size = 5958199, upload-time = "2026-01-31T23:11:35.385Z" }, + { url = "https://files.pythonhosted.org/packages/43/bc/6352f343522fcb2c04dbaf94cb30cca6fd32c1a750c06ad6231b4293708c/numpy-2.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622", size = 12310848, upload-time = "2026-01-31T23:11:38.001Z" }, + { url = "https://files.pythonhosted.org/packages/6e/8d/6da186483e308da5da1cc6918ce913dcfe14ffde98e710bfeff2a6158d4e/numpy-2.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71", size = 10221082, upload-time = "2026-01-31T23:11:40.392Z" }, + { url = "https://files.pythonhosted.org/packages/25/a1/9510aa43555b44781968935c7548a8926274f815de42ad3997e9e83680dd/numpy-2.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262", size = 14815866, upload-time = "2026-01-31T23:11:42.495Z" }, + { url = "https://files.pythonhosted.org/packages/36/30/6bbb5e76631a5ae46e7923dd16ca9d3f1c93cfa8d4ed79a129814a9d8db3/numpy-2.4.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913", size = 5325631, upload-time = "2026-01-31T23:11:44.7Z" }, + { url = "https://files.pythonhosted.org/packages/46/00/3a490938800c1923b567b3a15cd17896e68052e2145d8662aaf3e1ffc58f/numpy-2.4.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab", size = 6646254, upload-time = "2026-01-31T23:11:46.341Z" }, + { url = "https://files.pythonhosted.org/packages/d3/e9/fac0890149898a9b609caa5af7455a948b544746e4b8fe7c212c8edd71f8/numpy-2.4.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82", size = 15720138, upload-time = "2026-01-31T23:11:48.082Z" }, + { url = "https://files.pythonhosted.org/packages/ea/5c/08887c54e68e1e28df53709f1893ce92932cc6f01f7c3d4dc952f61ffd4e/numpy-2.4.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f", size = 16655398, upload-time = "2026-01-31T23:11:50.293Z" }, + { url = "https://files.pythonhosted.org/packages/4d/89/253db0fa0e66e9129c745e4ef25631dc37d5f1314dad2b53e907b8538e6d/numpy-2.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554", size = 17079064, upload-time = "2026-01-31T23:11:52.927Z" }, + { url = "https://files.pythonhosted.org/packages/2a/d5/cbade46ce97c59c6c3da525e8d95b7abe8a42974a1dc5c1d489c10433e88/numpy-2.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257", size = 18379680, upload-time = "2026-01-31T23:11:55.22Z" }, + { url = "https://files.pythonhosted.org/packages/40/62/48f99ae172a4b63d981babe683685030e8a3df4f246c893ea5c6ef99f018/numpy-2.4.2-cp313-cp313t-win32.whl", hash = "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657", size = 6082433, upload-time = "2026-01-31T23:11:58.096Z" }, + { url = "https://files.pythonhosted.org/packages/07/38/e054a61cfe48ad9f1ed0d188e78b7e26859d0b60ef21cd9de4897cdb5326/numpy-2.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b", size = 12451181, upload-time = "2026-01-31T23:11:59.782Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a4/a05c3a6418575e185dd84d0b9680b6bb2e2dc3e4202f036b7b4e22d6e9dc/numpy-2.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1", size = 10290756, upload-time = "2026-01-31T23:12:02.438Z" }, + { url = "https://files.pythonhosted.org/packages/18/88/b7df6050bf18fdcfb7046286c6535cabbdd2064a3440fca3f069d319c16e/numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b", size = 16663092, upload-time = "2026-01-31T23:12:04.521Z" }, + { url = "https://files.pythonhosted.org/packages/25/7a/1fee4329abc705a469a4afe6e69b1ef7e915117747886327104a8493a955/numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000", size = 14698770, upload-time = "2026-01-31T23:12:06.96Z" }, + { url = "https://files.pythonhosted.org/packages/fb/0b/f9e49ba6c923678ad5bc38181c08ac5e53b7a5754dbca8e581aa1a56b1ff/numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1", size = 5208562, upload-time = "2026-01-31T23:12:09.632Z" }, + { url = "https://files.pythonhosted.org/packages/7d/12/d7de8f6f53f9bb76997e5e4c069eda2051e3fe134e9181671c4391677bb2/numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74", size = 6543710, upload-time = "2026-01-31T23:12:11.969Z" }, + { url = "https://files.pythonhosted.org/packages/09/63/c66418c2e0268a31a4cf8a8b512685748200f8e8e8ec6c507ce14e773529/numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a", size = 15677205, upload-time = "2026-01-31T23:12:14.33Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6c/7f237821c9642fb2a04d2f1e88b4295677144ca93285fd76eff3bcba858d/numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325", size = 16611738, upload-time = "2026-01-31T23:12:16.525Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a7/39c4cdda9f019b609b5c473899d87abff092fc908cfe4d1ecb2fcff453b0/numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909", size = 17028888, upload-time = "2026-01-31T23:12:19.306Z" }, + { url = "https://files.pythonhosted.org/packages/da/b3/e84bb64bdfea967cc10950d71090ec2d84b49bc691df0025dddb7c26e8e3/numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a", size = 18339556, upload-time = "2026-01-31T23:12:21.816Z" }, + { url = "https://files.pythonhosted.org/packages/88/f5/954a291bc1192a27081706862ac62bb5920fbecfbaa302f64682aa90beed/numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a", size = 6006899, upload-time = "2026-01-31T23:12:24.14Z" }, + { url = "https://files.pythonhosted.org/packages/05/cb/eff72a91b2efdd1bc98b3b8759f6a1654aa87612fc86e3d87d6fe4f948c4/numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75", size = 12443072, upload-time = "2026-01-31T23:12:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/37/75/62726948db36a56428fce4ba80a115716dc4fad6a3a4352487f8bb950966/numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05", size = 10494886, upload-time = "2026-01-31T23:12:28.488Z" }, + { url = "https://files.pythonhosted.org/packages/36/2f/ee93744f1e0661dc267e4b21940870cabfae187c092e1433b77b09b50ac4/numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308", size = 14818567, upload-time = "2026-01-31T23:12:30.709Z" }, + { url = "https://files.pythonhosted.org/packages/a7/24/6535212add7d76ff938d8bdc654f53f88d35cddedf807a599e180dcb8e66/numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef", size = 5328372, upload-time = "2026-01-31T23:12:32.962Z" }, + { url = "https://files.pythonhosted.org/packages/5e/9d/c48f0a035725f925634bf6b8994253b43f2047f6778a54147d7e213bc5a7/numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d", size = 6649306, upload-time = "2026-01-31T23:12:34.797Z" }, + { url = "https://files.pythonhosted.org/packages/81/05/7c73a9574cd4a53a25907bad38b59ac83919c0ddc8234ec157f344d57d9a/numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8", size = 15722394, upload-time = "2026-01-31T23:12:36.565Z" }, + { url = "https://files.pythonhosted.org/packages/35/fa/4de10089f21fc7d18442c4a767ab156b25c2a6eaf187c0db6d9ecdaeb43f/numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5", size = 16653343, upload-time = "2026-01-31T23:12:39.188Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f9/d33e4ffc857f3763a57aa85650f2e82486832d7492280ac21ba9efda80da/numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e", size = 17078045, upload-time = "2026-01-31T23:12:42.041Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b8/54bdb43b6225badbea6389fa038c4ef868c44f5890f95dd530a218706da3/numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a", size = 18380024, upload-time = "2026-01-31T23:12:44.331Z" }, + { url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" }, + { url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" }, + { url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" }, ] [[package]] @@ -1264,123 +1288,128 @@ wheels = [ [[package]] name = "packaging" -version = "25.0" +version = "26.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, ] [[package]] name = "pandas" -version = "2.3.3" +version = "3.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, - { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, - { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, - { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, - { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, - { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, - { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, - { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, - { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, - { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, - { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, - { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, - { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, - { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, - { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, - { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, - { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, - { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, - { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, - { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, - { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, - { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, - { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, - { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, - { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/da/b1dc0481ab8d55d0f46e343cfe67d4551a0e14fcee52bd38ca1bd73258d8/pandas-3.0.0.tar.gz", hash = "sha256:0facf7e87d38f721f0af46fe70d97373a37701b1c09f7ed7aeeb292ade5c050f", size = 4633005, upload-time = "2026-01-21T15:52:04.726Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/fa/7f0ac4ca8877c57537aaff2a842f8760e630d8e824b730eb2e859ffe96ca/pandas-3.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b78d646249b9a2bc191040988c7bb524c92fa8534fb0898a0741d7e6f2ffafa6", size = 10307129, upload-time = "2026-01-21T15:50:52.877Z" }, + { url = "https://files.pythonhosted.org/packages/6f/11/28a221815dcea4c0c9414dfc845e34a84a6a7dabc6da3194498ed5ba4361/pandas-3.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bc9cba7b355cb4162442a88ce495e01cb605f17ac1e27d6596ac963504e0305f", size = 9850201, upload-time = "2026-01-21T15:50:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/ba/da/53bbc8c5363b7e5bd10f9ae59ab250fc7a382ea6ba08e4d06d8694370354/pandas-3.0.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c9a1a149aed3b6c9bf246033ff91e1b02d529546c5d6fb6b74a28fea0cf4c70", size = 10354031, upload-time = "2026-01-21T15:50:57.463Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a3/51e02ebc2a14974170d51e2410dfdab58870ea9bcd37cda15bd553d24dc4/pandas-3.0.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95683af6175d884ee89471842acfca29172a85031fccdabc35e50c0984470a0e", size = 10861165, upload-time = "2026-01-21T15:50:59.32Z" }, + { url = "https://files.pythonhosted.org/packages/a5/fe/05a51e3cac11d161472b8297bd41723ea98013384dd6d76d115ce3482f9b/pandas-3.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1fbbb5a7288719e36b76b4f18d46ede46e7f916b6c8d9915b756b0a6c3f792b3", size = 11359359, upload-time = "2026-01-21T15:51:02.014Z" }, + { url = "https://files.pythonhosted.org/packages/ee/56/ba620583225f9b85a4d3e69c01df3e3870659cc525f67929b60e9f21dcd1/pandas-3.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e8b9808590fa364416b49b2a35c1f4cf2785a6c156935879e57f826df22038e", size = 11912907, upload-time = "2026-01-21T15:51:05.175Z" }, + { url = "https://files.pythonhosted.org/packages/c9/8c/c6638d9f67e45e07656b3826405c5cc5f57f6fd07c8b2572ade328c86e22/pandas-3.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:98212a38a709feb90ae658cb6227ea3657c22ba8157d4b8f913cd4c950de5e7e", size = 9732138, upload-time = "2026-01-21T15:51:07.569Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bf/bd1335c3bf1770b6d8fed2799993b11c4971af93bb1b729b9ebbc02ca2ec/pandas-3.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:177d9df10b3f43b70307a149d7ec49a1229a653f907aa60a48f1877d0e6be3be", size = 9033568, upload-time = "2026-01-21T15:51:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/8e/c6/f5e2171914d5e29b9171d495344097d54e3ffe41d2d85d8115baba4dc483/pandas-3.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2713810ad3806767b89ad3b7b69ba153e1c6ff6d9c20f9c2140379b2a98b6c98", size = 10741936, upload-time = "2026-01-21T15:51:11.693Z" }, + { url = "https://files.pythonhosted.org/packages/51/88/9a0164f99510a1acb9f548691f022c756c2314aad0d8330a24616c14c462/pandas-3.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:15d59f885ee5011daf8335dff47dcb8a912a27b4ad7826dc6cbe809fd145d327", size = 10393884, upload-time = "2026-01-21T15:51:14.197Z" }, + { url = "https://files.pythonhosted.org/packages/e0/53/b34d78084d88d8ae2b848591229da8826d1e65aacf00b3abe34023467648/pandas-3.0.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24e6547fb64d2c92665dd2adbfa4e85fa4fd70a9c070e7cfb03b629a0bbab5eb", size = 10310740, upload-time = "2026-01-21T15:51:16.093Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d3/bee792e7c3d6930b74468d990604325701412e55d7aaf47460a22311d1a5/pandas-3.0.0-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48ee04b90e2505c693d3f8e8f524dab8cb8aaf7ddcab52c92afa535e717c4812", size = 10700014, upload-time = "2026-01-21T15:51:18.818Z" }, + { url = "https://files.pythonhosted.org/packages/55/db/2570bc40fb13aaed1cbc3fbd725c3a60ee162477982123c3adc8971e7ac1/pandas-3.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66f72fb172959af42a459e27a8d8d2c7e311ff4c1f7db6deb3b643dbc382ae08", size = 11323737, upload-time = "2026-01-21T15:51:20.784Z" }, + { url = "https://files.pythonhosted.org/packages/bc/2e/297ac7f21c8181b62a4cccebad0a70caf679adf3ae5e83cb676194c8acc3/pandas-3.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4a4a400ca18230976724a5066f20878af785f36c6756e498e94c2a5e5d57779c", size = 11771558, upload-time = "2026-01-21T15:51:22.977Z" }, + { url = "https://files.pythonhosted.org/packages/0a/46/e1c6876d71c14332be70239acce9ad435975a80541086e5ffba2f249bcf6/pandas-3.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:940eebffe55528074341a5a36515f3e4c5e25e958ebbc764c9502cfc35ba3faa", size = 10473771, upload-time = "2026-01-21T15:51:25.285Z" }, + { url = "https://files.pythonhosted.org/packages/c0/db/0270ad9d13c344b7a36fa77f5f8344a46501abf413803e885d22864d10bf/pandas-3.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:597c08fb9fef0edf1e4fa2f9828dd27f3d78f9b8c9b4a748d435ffc55732310b", size = 10312075, upload-time = "2026-01-21T15:51:28.5Z" }, + { url = "https://files.pythonhosted.org/packages/09/9f/c176f5e9717f7c91becfe0f55a52ae445d3f7326b4a2cf355978c51b7913/pandas-3.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:447b2d68ac5edcbf94655fe909113a6dba6ef09ad7f9f60c80477825b6c489fe", size = 9900213, upload-time = "2026-01-21T15:51:30.955Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e7/63ad4cc10b257b143e0a5ebb04304ad806b4e1a61c5da25f55896d2ca0f4/pandas-3.0.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:debb95c77ff3ed3ba0d9aa20c3a2f19165cc7956362f9873fce1ba0a53819d70", size = 10428768, upload-time = "2026-01-21T15:51:33.018Z" }, + { url = "https://files.pythonhosted.org/packages/9e/0e/4e4c2d8210f20149fd2248ef3fff26623604922bd564d915f935a06dd63d/pandas-3.0.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fedabf175e7cd82b69b74c30adbaa616de301291a5231138d7242596fc296a8d", size = 10882954, upload-time = "2026-01-21T15:51:35.287Z" }, + { url = "https://files.pythonhosted.org/packages/c6/60/c9de8ac906ba1f4d2250f8a951abe5135b404227a55858a75ad26f84db47/pandas-3.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:412d1a89aab46889f3033a386912efcdfa0f1131c5705ff5b668dda88305e986", size = 11430293, upload-time = "2026-01-21T15:51:37.57Z" }, + { url = "https://files.pythonhosted.org/packages/a1/69/806e6637c70920e5787a6d6896fd707f8134c2c55cd761e7249a97b7dc5a/pandas-3.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e979d22316f9350c516479dd3a92252be2937a9531ed3a26ec324198a99cdd49", size = 11952452, upload-time = "2026-01-21T15:51:39.618Z" }, + { url = "https://files.pythonhosted.org/packages/cb/de/918621e46af55164c400ab0ef389c9d969ab85a43d59ad1207d4ddbe30a5/pandas-3.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:083b11415b9970b6e7888800c43c82e81a06cd6b06755d84804444f0007d6bb7", size = 9851081, upload-time = "2026-01-21T15:51:41.758Z" }, + { url = "https://files.pythonhosted.org/packages/91/a1/3562a18dd0bd8c73344bfa26ff90c53c72f827df119d6d6b1dacc84d13e3/pandas-3.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:5db1e62cb99e739fa78a28047e861b256d17f88463c76b8dafc7c1338086dca8", size = 9174610, upload-time = "2026-01-21T15:51:44.312Z" }, + { url = "https://files.pythonhosted.org/packages/ce/26/430d91257eaf366f1737d7a1c158677caaf6267f338ec74e3a1ec444111c/pandas-3.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:697b8f7d346c68274b1b93a170a70974cdc7d7354429894d5927c1effdcccd73", size = 10761999, upload-time = "2026-01-21T15:51:46.899Z" }, + { url = "https://files.pythonhosted.org/packages/ec/1a/954eb47736c2b7f7fe6a9d56b0cb6987773c00faa3c6451a43db4beb3254/pandas-3.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8cb3120f0d9467ed95e77f67a75e030b67545bcfa08964e349252d674171def2", size = 10410279, upload-time = "2026-01-21T15:51:48.89Z" }, + { url = "https://files.pythonhosted.org/packages/20/fc/b96f3a5a28b250cd1b366eb0108df2501c0f38314a00847242abab71bb3a/pandas-3.0.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33fd3e6baa72899746b820c31e4b9688c8e1b7864d7aec2de7ab5035c285277a", size = 10330198, upload-time = "2026-01-21T15:51:51.015Z" }, + { url = "https://files.pythonhosted.org/packages/90/b3/d0e2952f103b4fbef1ef22d0c2e314e74fc9064b51cee30890b5e3286ee6/pandas-3.0.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8942e333dc67ceda1095227ad0febb05a3b36535e520154085db632c40ad084", size = 10728513, upload-time = "2026-01-21T15:51:53.387Z" }, + { url = "https://files.pythonhosted.org/packages/76/81/832894f286df828993dc5fd61c63b231b0fb73377e99f6c6c369174cf97e/pandas-3.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:783ac35c4d0fe0effdb0d67161859078618b1b6587a1af15928137525217a721", size = 11345550, upload-time = "2026-01-21T15:51:55.329Z" }, + { url = "https://files.pythonhosted.org/packages/34/a0/ed160a00fb4f37d806406bc0a79a8b62fe67f29d00950f8d16203ff3409b/pandas-3.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:125eb901e233f155b268bbef9abd9afb5819db74f0e677e89a61b246228c71ac", size = 11799386, upload-time = "2026-01-21T15:51:57.457Z" }, + { url = "https://files.pythonhosted.org/packages/36/c8/2ac00d7255252c5e3cf61b35ca92ca25704b0188f7454ca4aec08a33cece/pandas-3.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b86d113b6c109df3ce0ad5abbc259fe86a1bd4adfd4a31a89da42f84f65509bb", size = 10873041, upload-time = "2026-01-21T15:52:00.034Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3f/a80ac00acbc6b35166b42850e98a4f466e2c0d9c64054161ba9620f95680/pandas-3.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1c39eab3ad38f2d7a249095f0a3d8f8c22cc0f847e98ccf5bbe732b272e2d9fa", size = 9441003, upload-time = "2026-01-21T15:52:02.281Z" }, ] [[package]] name = "paracelsus" -version = "0.13.2" +version = "0.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "packaging" }, { name = "pydot" }, { name = "sqlalchemy" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/03/ca/adad895f85086a2942d4a47b02d0df02d99db3bd6adc904c796c487eb110/paracelsus-0.13.2.tar.gz", hash = "sha256:1865e68a6cd56e8c1a003266abfe2d4f7d8ec187c8649098d12c2ba8d4f8b48a", size = 86625, upload-time = "2025-11-22T01:36:30.514Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/cc/d545a19967c3bdeba92ca1d8a736576b96b4610154f3bd6dbf01a198e2c3/paracelsus-0.15.0.tar.gz", hash = "sha256:b850b56417eef7b5e301b09ba7d44655f3c76de8681699b93ef6ae410afeb278", size = 92053, upload-time = "2026-01-04T21:38:25.508Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/0e/7a7ba5b69bd6e75533e5ae046e359a74008366139d8fd36fcbfe3ac5923a/paracelsus-0.13.2-py3-none-any.whl", hash = "sha256:330782a682225f2ece59e29d7cc93ab902d177889d88eb2a97efa09a2fd9cc45", size = 15837, upload-time = "2025-11-22T01:36:29.12Z" }, + { url = "https://files.pythonhosted.org/packages/18/70/3fa8dad530ae181b0a30f9874bababaa3d3781f9ef6c87aeaeed79b3c954/paracelsus-0.15.0-py3-none-any.whl", hash = "sha256:0ed0f97fb5ec09e379e45c1a95e280b1c40ee42af3c77f59f03998477a73fde2", size = 19606, upload-time = "2026-01-04T21:38:24.284Z" }, ] [[package]] name = "pillow" -version = "12.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828, upload-time = "2025-10-15T18:24:14.008Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/f2/de993bb2d21b33a98d031ecf6a978e4b61da207bef02f7b43093774c480d/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", size = 4045493, upload-time = "2025-10-15T18:22:25.758Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b6/bc8d0c4c9f6f111a783d045310945deb769b806d7574764234ffd50bc5ea/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", size = 4120461, upload-time = "2025-10-15T18:22:27.286Z" }, - { url = "https://files.pythonhosted.org/packages/5d/57/d60d343709366a353dc56adb4ee1e7d8a2cc34e3fbc22905f4167cfec119/pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", size = 3576912, upload-time = "2025-10-15T18:22:28.751Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a4/a0a31467e3f83b94d37568294b01d22b43ae3c5d85f2811769b9c66389dd/pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", size = 5249132, upload-time = "2025-10-15T18:22:30.641Z" }, - { url = "https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", size = 4650099, upload-time = "2025-10-15T18:22:32.73Z" }, - { url = "https://files.pythonhosted.org/packages/fc/bd/69ed99fd46a8dba7c1887156d3572fe4484e3f031405fcc5a92e31c04035/pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", size = 6230808, upload-time = "2025-10-15T18:22:34.337Z" }, - { url = "https://files.pythonhosted.org/packages/ea/94/8fad659bcdbf86ed70099cb60ae40be6acca434bbc8c4c0d4ef356d7e0de/pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", size = 8037804, upload-time = "2025-10-15T18:22:36.402Z" }, - { url = "https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", size = 6345553, upload-time = "2025-10-15T18:22:38.066Z" }, - { url = "https://files.pythonhosted.org/packages/38/57/755dbd06530a27a5ed74f8cb0a7a44a21722ebf318edbe67ddbd7fb28f88/pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", size = 7037729, upload-time = "2025-10-15T18:22:39.769Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b6/7e94f4c41d238615674d06ed677c14883103dce1c52e4af16f000338cfd7/pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", size = 6459789, upload-time = "2025-10-15T18:22:41.437Z" }, - { url = "https://files.pythonhosted.org/packages/9c/14/4448bb0b5e0f22dd865290536d20ec8a23b64e2d04280b89139f09a36bb6/pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", size = 7130917, upload-time = "2025-10-15T18:22:43.152Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ca/16c6926cc1c015845745d5c16c9358e24282f1e588237a4c36d2b30f182f/pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", size = 6302391, upload-time = "2025-10-15T18:22:44.753Z" }, - { url = "https://files.pythonhosted.org/packages/6d/2a/dd43dcfd6dae9b6a49ee28a8eedb98c7d5ff2de94a5d834565164667b97b/pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", size = 7007477, upload-time = "2025-10-15T18:22:46.838Z" }, - { url = "https://files.pythonhosted.org/packages/77/f0/72ea067f4b5ae5ead653053212af05ce3705807906ba3f3e8f58ddf617e6/pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", size = 2435918, upload-time = "2025-10-15T18:22:48.399Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5e/9046b423735c21f0487ea6cb5b10f89ea8f8dfbe32576fe052b5ba9d4e5b/pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", size = 5251406, upload-time = "2025-10-15T18:22:49.905Z" }, - { url = "https://files.pythonhosted.org/packages/12/66/982ceebcdb13c97270ef7a56c3969635b4ee7cd45227fa707c94719229c5/pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", size = 4653218, upload-time = "2025-10-15T18:22:51.587Z" }, - { url = "https://files.pythonhosted.org/packages/16/b3/81e625524688c31859450119bf12674619429cab3119eec0e30a7a1029cb/pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", size = 6266564, upload-time = "2025-10-15T18:22:53.215Z" }, - { url = "https://files.pythonhosted.org/packages/98/59/dfb38f2a41240d2408096e1a76c671d0a105a4a8471b1871c6902719450c/pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", size = 8069260, upload-time = "2025-10-15T18:22:54.933Z" }, - { url = "https://files.pythonhosted.org/packages/dc/3d/378dbea5cd1874b94c312425ca77b0f47776c78e0df2df751b820c8c1d6c/pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", size = 6379248, upload-time = "2025-10-15T18:22:56.605Z" }, - { url = "https://files.pythonhosted.org/packages/84/b0/d525ef47d71590f1621510327acec75ae58c721dc071b17d8d652ca494d8/pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", size = 7066043, upload-time = "2025-10-15T18:22:58.53Z" }, - { url = "https://files.pythonhosted.org/packages/61/2c/aced60e9cf9d0cde341d54bf7932c9ffc33ddb4a1595798b3a5150c7ec4e/pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", size = 6490915, upload-time = "2025-10-15T18:23:00.582Z" }, - { url = "https://files.pythonhosted.org/packages/ef/26/69dcb9b91f4e59f8f34b2332a4a0a951b44f547c4ed39d3e4dcfcff48f89/pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", size = 7157998, upload-time = "2025-10-15T18:23:02.627Z" }, - { url = "https://files.pythonhosted.org/packages/61/2b/726235842220ca95fa441ddf55dd2382b52ab5b8d9c0596fe6b3f23dafe8/pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", size = 6306201, upload-time = "2025-10-15T18:23:04.709Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3d/2afaf4e840b2df71344ababf2f8edd75a705ce500e5dc1e7227808312ae1/pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", size = 7013165, upload-time = "2025-10-15T18:23:06.46Z" }, - { url = "https://files.pythonhosted.org/packages/6f/75/3fa09aa5cf6ed04bee3fa575798ddf1ce0bace8edb47249c798077a81f7f/pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", size = 2437834, upload-time = "2025-10-15T18:23:08.194Z" }, - { url = "https://files.pythonhosted.org/packages/54/2a/9a8c6ba2c2c07b71bec92cf63e03370ca5e5f5c5b119b742bcc0cde3f9c5/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", size = 4045531, upload-time = "2025-10-15T18:23:10.121Z" }, - { url = "https://files.pythonhosted.org/packages/84/54/836fdbf1bfb3d66a59f0189ff0b9f5f666cee09c6188309300df04ad71fa/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", size = 4120554, upload-time = "2025-10-15T18:23:12.14Z" }, - { url = "https://files.pythonhosted.org/packages/0d/cd/16aec9f0da4793e98e6b54778a5fbce4f375c6646fe662e80600b8797379/pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", size = 3576812, upload-time = "2025-10-15T18:23:13.962Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b7/13957fda356dc46339298b351cae0d327704986337c3c69bb54628c88155/pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", size = 5252689, upload-time = "2025-10-15T18:23:15.562Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f5/eae31a306341d8f331f43edb2e9122c7661b975433de5e447939ae61c5da/pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", size = 4650186, upload-time = "2025-10-15T18:23:17.379Z" }, - { url = "https://files.pythonhosted.org/packages/86/62/2a88339aa40c4c77e79108facbd307d6091e2c0eb5b8d3cf4977cfca2fe6/pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", size = 6230308, upload-time = "2025-10-15T18:23:18.971Z" }, - { url = "https://files.pythonhosted.org/packages/c7/33/5425a8992bcb32d1cb9fa3dd39a89e613d09a22f2c8083b7bf43c455f760/pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", size = 8039222, upload-time = "2025-10-15T18:23:20.909Z" }, - { url = "https://files.pythonhosted.org/packages/d8/61/3f5d3b35c5728f37953d3eec5b5f3e77111949523bd2dd7f31a851e50690/pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", size = 6346657, upload-time = "2025-10-15T18:23:23.077Z" }, - { url = "https://files.pythonhosted.org/packages/3a/be/ee90a3d79271227e0f0a33c453531efd6ed14b2e708596ba5dd9be948da3/pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", size = 7038482, upload-time = "2025-10-15T18:23:25.005Z" }, - { url = "https://files.pythonhosted.org/packages/44/34/a16b6a4d1ad727de390e9bd9f19f5f669e079e5826ec0f329010ddea492f/pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", size = 6461416, upload-time = "2025-10-15T18:23:27.009Z" }, - { url = "https://files.pythonhosted.org/packages/b6/39/1aa5850d2ade7d7ba9f54e4e4c17077244ff7a2d9e25998c38a29749eb3f/pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", size = 7131584, upload-time = "2025-10-15T18:23:29.752Z" }, - { url = "https://files.pythonhosted.org/packages/bf/db/4fae862f8fad0167073a7733973bfa955f47e2cac3dc3e3e6257d10fab4a/pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", size = 6400621, upload-time = "2025-10-15T18:23:32.06Z" }, - { url = "https://files.pythonhosted.org/packages/2b/24/b350c31543fb0107ab2599464d7e28e6f856027aadda995022e695313d94/pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", size = 7142916, upload-time = "2025-10-15T18:23:34.71Z" }, - { url = "https://files.pythonhosted.org/packages/0f/9b/0ba5a6fd9351793996ef7487c4fdbde8d3f5f75dbedc093bb598648fddf0/pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", size = 2523836, upload-time = "2025-10-15T18:23:36.967Z" }, - { url = "https://files.pythonhosted.org/packages/f5/7a/ceee0840aebc579af529b523d530840338ecf63992395842e54edc805987/pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", size = 5255092, upload-time = "2025-10-15T18:23:38.573Z" }, - { url = "https://files.pythonhosted.org/packages/44/76/20776057b4bfd1aef4eeca992ebde0f53a4dce874f3ae693d0ec90a4f79b/pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", size = 4653158, upload-time = "2025-10-15T18:23:40.238Z" }, - { url = "https://files.pythonhosted.org/packages/82/3f/d9ff92ace07be8836b4e7e87e6a4c7a8318d47c2f1463ffcf121fc57d9cb/pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1", size = 6267882, upload-time = "2025-10-15T18:23:42.434Z" }, - { url = "https://files.pythonhosted.org/packages/9f/7a/4f7ff87f00d3ad33ba21af78bfcd2f032107710baf8280e3722ceec28cda/pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", size = 8071001, upload-time = "2025-10-15T18:23:44.29Z" }, - { url = "https://files.pythonhosted.org/packages/75/87/fcea108944a52dad8cca0715ae6247e271eb80459364a98518f1e4f480c1/pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", size = 6380146, upload-time = "2025-10-15T18:23:46.065Z" }, - { url = "https://files.pythonhosted.org/packages/91/52/0d31b5e571ef5fd111d2978b84603fce26aba1b6092f28e941cb46570745/pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", size = 7067344, upload-time = "2025-10-15T18:23:47.898Z" }, - { url = "https://files.pythonhosted.org/packages/7b/f4/2dd3d721f875f928d48e83bb30a434dee75a2531bca839bb996bb0aa5a91/pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", size = 6491864, upload-time = "2025-10-15T18:23:49.607Z" }, - { url = "https://files.pythonhosted.org/packages/30/4b/667dfcf3d61fc309ba5a15b141845cece5915e39b99c1ceab0f34bf1d124/pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", size = 7158911, upload-time = "2025-10-15T18:23:51.351Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2f/16cabcc6426c32218ace36bf0d55955e813f2958afddbf1d391849fee9d1/pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", size = 6408045, upload-time = "2025-10-15T18:23:53.177Z" }, - { url = "https://files.pythonhosted.org/packages/35/73/e29aa0c9c666cf787628d3f0dcf379f4791fba79f4936d02f8b37165bdf8/pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", size = 7148282, upload-time = "2025-10-15T18:23:55.316Z" }, - { url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630, upload-time = "2025-10-15T18:23:57.149Z" }, +version = "12.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/42/5c74462b4fd957fcd7b13b04fb3205ff8349236ea74c7c375766d6c82288/pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4", size = 46980264, upload-time = "2026-02-11T04:23:07.146Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/11/6db24d4bd7685583caeae54b7009584e38da3c3d4488ed4cd25b439de486/pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d242e8ac078781f1de88bf823d70c1a9b3c7950a44cdf4b7c012e22ccbcd8e4e", size = 4062689, upload-time = "2026-02-11T04:21:06.804Z" }, + { url = "https://files.pythonhosted.org/packages/33/c0/ce6d3b1fe190f0021203e0d9b5b99e57843e345f15f9ef22fcd43842fd21/pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:02f84dfad02693676692746df05b89cf25597560db2857363a208e393429f5e9", size = 4138535, upload-time = "2026-02-11T04:21:08.452Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c6/d5eb6a4fb32a3f9c21a8c7613ec706534ea1cf9f4b3663e99f0d83f6fca8/pillow-12.1.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e65498daf4b583091ccbb2556c7000abf0f3349fcd57ef7adc9a84a394ed29f6", size = 3601364, upload-time = "2026-02-11T04:21:10.194Z" }, + { url = "https://files.pythonhosted.org/packages/14/a1/16c4b823838ba4c9c52c0e6bbda903a3fe5a1bdbf1b8eb4fff7156f3e318/pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60", size = 5262561, upload-time = "2026-02-11T04:21:11.742Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ad/ad9dc98ff24f485008aa5cdedaf1a219876f6f6c42a4626c08bc4e80b120/pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2", size = 4657460, upload-time = "2026-02-11T04:21:13.786Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f1a4ea9a895b5732152789326202a82464d5254759fbacae4deea3069334/pillow-12.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5bddd742a44b7e6b1e773ab5db102bd7a94c32555ba656e76d319d19c3850", size = 6232698, upload-time = "2026-02-11T04:21:15.949Z" }, + { url = "https://files.pythonhosted.org/packages/95/f4/86f51b8745070daf21fd2e5b1fe0eb35d4db9ca26e6d58366562fb56a743/pillow-12.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc44ef1f3de4f45b50ccf9136999d71abb99dca7706bc75d222ed350b9fd2289", size = 8041706, upload-time = "2026-02-11T04:21:17.723Z" }, + { url = "https://files.pythonhosted.org/packages/29/9b/d6ecd956bb1266dd1045e995cce9b8d77759e740953a1c9aad9502a0461e/pillow-12.1.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a8eb7ed8d4198bccbd07058416eeec51686b498e784eda166395a23eb99138e", size = 6346621, upload-time = "2026-02-11T04:21:19.547Z" }, + { url = "https://files.pythonhosted.org/packages/71/24/538bff45bde96535d7d998c6fed1a751c75ac7c53c37c90dc2601b243893/pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717", size = 7038069, upload-time = "2026-02-11T04:21:21.378Z" }, + { url = "https://files.pythonhosted.org/packages/94/0e/58cb1a6bc48f746bc4cb3adb8cabff73e2742c92b3bf7a220b7cf69b9177/pillow-12.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:518a48c2aab7ce596d3bf79d0e275661b846e86e4d0e7dec34712c30fe07f02a", size = 6460040, upload-time = "2026-02-11T04:21:23.148Z" }, + { url = "https://files.pythonhosted.org/packages/6c/57/9045cb3ff11eeb6c1adce3b2d60d7d299d7b273a2e6c8381a524abfdc474/pillow-12.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a550ae29b95c6dc13cf69e2c9dc5747f814c54eeb2e32d683e5e93af56caa029", size = 7164523, upload-time = "2026-02-11T04:21:25.01Z" }, + { url = "https://files.pythonhosted.org/packages/73/f2/9be9cb99f2175f0d4dbadd6616ce1bf068ee54a28277ea1bf1fbf729c250/pillow-12.1.1-cp313-cp313-win32.whl", hash = "sha256:a003d7422449f6d1e3a34e3dd4110c22148336918ddbfc6a32581cd54b2e0b2b", size = 6332552, upload-time = "2026-02-11T04:21:27.238Z" }, + { url = "https://files.pythonhosted.org/packages/3f/eb/b0834ad8b583d7d9d42b80becff092082a1c3c156bb582590fcc973f1c7c/pillow-12.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:344cf1e3dab3be4b1fa08e449323d98a2a3f819ad20f4b22e77a0ede31f0faa1", size = 7040108, upload-time = "2026-02-11T04:21:29.462Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7d/fc09634e2aabdd0feabaff4a32f4a7d97789223e7c2042fd805ea4b4d2c2/pillow-12.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c0dd1636633e7e6a0afe7bf6a51a14992b7f8e60de5789018ebbdfae55b040a", size = 2453712, upload-time = "2026-02-11T04:21:31.072Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/b9d62794fc8a0dd14c1943df68347badbd5511103e0d04c035ffe5cf2255/pillow-12.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0330d233c1a0ead844fc097a7d16c0abff4c12e856c0b325f231820fee1f39da", size = 5264880, upload-time = "2026-02-11T04:21:32.865Z" }, + { url = "https://files.pythonhosted.org/packages/26/9d/e03d857d1347fa5ed9247e123fcd2a97b6220e15e9cb73ca0a8d91702c6e/pillow-12.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dae5f21afb91322f2ff791895ddd8889e5e947ff59f71b46041c8ce6db790bc", size = 4660616, upload-time = "2026-02-11T04:21:34.97Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ec/8a6d22afd02570d30954e043f09c32772bfe143ba9285e2fdb11284952cd/pillow-12.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e0c664be47252947d870ac0d327fea7e63985a08794758aa8af5b6cb6ec0c9c", size = 6269008, upload-time = "2026-02-11T04:21:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/3d/1d/6d875422c9f28a4a361f495a5f68d9de4a66941dc2c619103ca335fa6446/pillow-12.1.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:691ab2ac363b8217f7d31b3497108fb1f50faab2f75dfb03284ec2f217e87bf8", size = 8073226, upload-time = "2026-02-11T04:21:38.585Z" }, + { url = "https://files.pythonhosted.org/packages/a1/cd/134b0b6ee5eda6dc09e25e24b40fdafe11a520bc725c1d0bbaa5e00bf95b/pillow-12.1.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9e8064fb1cc019296958595f6db671fba95209e3ceb0c4734c9baf97de04b20", size = 6380136, upload-time = "2026-02-11T04:21:40.562Z" }, + { url = "https://files.pythonhosted.org/packages/7a/a9/7628f013f18f001c1b98d8fffe3452f306a70dc6aba7d931019e0492f45e/pillow-12.1.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:472a8d7ded663e6162dafdf20015c486a7009483ca671cece7a9279b512fcb13", size = 7067129, upload-time = "2026-02-11T04:21:42.521Z" }, + { url = "https://files.pythonhosted.org/packages/1e/f8/66ab30a2193b277785601e82ee2d49f68ea575d9637e5e234faaa98efa4c/pillow-12.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:89b54027a766529136a06cfebeecb3a04900397a3590fd252160b888479517bf", size = 6491807, upload-time = "2026-02-11T04:21:44.22Z" }, + { url = "https://files.pythonhosted.org/packages/da/0b/a877a6627dc8318fdb84e357c5e1a758c0941ab1ddffdafd231983788579/pillow-12.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:86172b0831b82ce4f7877f280055892b31179e1576aa00d0df3bb1bbf8c3e524", size = 7190954, upload-time = "2026-02-11T04:21:46.114Z" }, + { url = "https://files.pythonhosted.org/packages/83/43/6f732ff85743cf746b1361b91665d9f5155e1483817f693f8d57ea93147f/pillow-12.1.1-cp313-cp313t-win32.whl", hash = "sha256:44ce27545b6efcf0fdbdceb31c9a5bdea9333e664cda58a7e674bb74608b3986", size = 6336441, upload-time = "2026-02-11T04:21:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/3b/44/e865ef3986611bb75bfabdf94a590016ea327833f434558801122979cd0e/pillow-12.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a285e3eb7a5a45a2ff504e31f4a8d1b12ef62e84e5411c6804a42197c1cf586c", size = 7045383, upload-time = "2026-02-11T04:21:50.015Z" }, + { url = "https://files.pythonhosted.org/packages/a8/c6/f4fb24268d0c6908b9f04143697ea18b0379490cb74ba9e8d41b898bd005/pillow-12.1.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cc7d296b5ea4d29e6570dabeaed58d31c3fea35a633a69679fb03d7664f43fb3", size = 2456104, upload-time = "2026-02-11T04:21:51.633Z" }, + { url = "https://files.pythonhosted.org/packages/03/d0/bebb3ffbf31c5a8e97241476c4cf8b9828954693ce6744b4a2326af3e16b/pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:417423db963cb4be8bac3fc1204fe61610f6abeed1580a7a2cbb2fbda20f12af", size = 4062652, upload-time = "2026-02-11T04:21:53.19Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c0/0e16fb0addda4851445c28f8350d8c512f09de27bbb0d6d0bbf8b6709605/pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:b957b71c6b2387610f556a7eb0828afbe40b4a98036fc0d2acfa5a44a0c2036f", size = 4138823, upload-time = "2026-02-11T04:22:03.088Z" }, + { url = "https://files.pythonhosted.org/packages/6b/fb/6170ec655d6f6bb6630a013dd7cf7bc218423d7b5fa9071bf63dc32175ae/pillow-12.1.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:097690ba1f2efdeb165a20469d59d8bb03c55fb6621eb2041a060ae8ea3e9642", size = 3601143, upload-time = "2026-02-11T04:22:04.909Z" }, + { url = "https://files.pythonhosted.org/packages/59/04/dc5c3f297510ba9a6837cbb318b87dd2b8f73eb41a43cc63767f65cb599c/pillow-12.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2815a87ab27848db0321fb78c7f0b2c8649dee134b7f2b80c6a45c6831d75ccd", size = 5266254, upload-time = "2026-02-11T04:22:07.656Z" }, + { url = "https://files.pythonhosted.org/packages/05/30/5db1236b0d6313f03ebf97f5e17cda9ca060f524b2fcc875149a8360b21c/pillow-12.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7ed2c6543bad5a7d5530eb9e78c53132f93dfa44a28492db88b41cdab885202", size = 4657499, upload-time = "2026-02-11T04:22:09.613Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/008d2ca0eb612e81968e8be0bbae5051efba24d52debf930126d7eaacbba/pillow-12.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:652a2c9ccfb556235b2b501a3a7cf3742148cd22e04b5625c5fe057ea3e3191f", size = 6232137, upload-time = "2026-02-11T04:22:11.434Z" }, + { url = "https://files.pythonhosted.org/packages/70/f1/f14d5b8eeb4b2cd62b9f9f847eb6605f103df89ef619ac68f92f748614ea/pillow-12.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6e4571eedf43af33d0fc233a382a76e849badbccdf1ac438841308652a08e1f", size = 8042721, upload-time = "2026-02-11T04:22:13.321Z" }, + { url = "https://files.pythonhosted.org/packages/5a/d6/17824509146e4babbdabf04d8171491fa9d776f7061ff6e727522df9bd03/pillow-12.1.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b574c51cf7d5d62e9be37ba446224b59a2da26dc4c1bb2ecbe936a4fb1a7cb7f", size = 6347798, upload-time = "2026-02-11T04:22:15.449Z" }, + { url = "https://files.pythonhosted.org/packages/d1/ee/c85a38a9ab92037a75615aba572c85ea51e605265036e00c5b67dfafbfe2/pillow-12.1.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a37691702ed687799de29a518d63d4682d9016932db66d4e90c345831b02fb4e", size = 7039315, upload-time = "2026-02-11T04:22:17.24Z" }, + { url = "https://files.pythonhosted.org/packages/ec/f3/bc8ccc6e08a148290d7523bde4d9a0d6c981db34631390dc6e6ec34cacf6/pillow-12.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f95c00d5d6700b2b890479664a06e754974848afaae5e21beb4d83c106923fd0", size = 6462360, upload-time = "2026-02-11T04:22:19.111Z" }, + { url = "https://files.pythonhosted.org/packages/f6/ab/69a42656adb1d0665ab051eec58a41f169ad295cf81ad45406963105408f/pillow-12.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:559b38da23606e68681337ad74622c4dbba02254fc9cb4488a305dd5975c7eeb", size = 7165438, upload-time = "2026-02-11T04:22:21.041Z" }, + { url = "https://files.pythonhosted.org/packages/02/46/81f7aa8941873f0f01d4b55cc543b0a3d03ec2ee30d617a0448bf6bd6dec/pillow-12.1.1-cp314-cp314-win32.whl", hash = "sha256:03edcc34d688572014ff223c125a3f77fb08091e4607e7745002fc214070b35f", size = 6431503, upload-time = "2026-02-11T04:22:22.833Z" }, + { url = "https://files.pythonhosted.org/packages/40/72/4c245f7d1044b67affc7f134a09ea619d4895333d35322b775b928180044/pillow-12.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:50480dcd74fa63b8e78235957d302d98d98d82ccbfac4c7e12108ba9ecbdba15", size = 7176748, upload-time = "2026-02-11T04:22:24.64Z" }, + { url = "https://files.pythonhosted.org/packages/e4/ad/8a87bdbe038c5c698736e3348af5c2194ffb872ea52f11894c95f9305435/pillow-12.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:5cb1785d97b0c3d1d1a16bc1d710c4a0049daefc4935f3a8f31f827f4d3d2e7f", size = 2544314, upload-time = "2026-02-11T04:22:26.685Z" }, + { url = "https://files.pythonhosted.org/packages/6c/9d/efd18493f9de13b87ede7c47e69184b9e859e4427225ea962e32e56a49bc/pillow-12.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1f90cff8aa76835cba5769f0b3121a22bd4eb9e6884cfe338216e557a9a548b8", size = 5268612, upload-time = "2026-02-11T04:22:29.884Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f1/4f42eb2b388eb2ffc660dcb7f7b556c1015c53ebd5f7f754965ef997585b/pillow-12.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1f1be78ce9466a7ee64bfda57bdba0f7cc499d9794d518b854816c41bf0aa4e9", size = 4660567, upload-time = "2026-02-11T04:22:31.799Z" }, + { url = "https://files.pythonhosted.org/packages/01/54/df6ef130fa43e4b82e32624a7b821a2be1c5653a5fdad8469687a7db4e00/pillow-12.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42fc1f4677106188ad9a55562bbade416f8b55456f522430fadab3cef7cd4e60", size = 6269951, upload-time = "2026-02-11T04:22:33.921Z" }, + { url = "https://files.pythonhosted.org/packages/a9/48/618752d06cc44bb4aae8ce0cd4e6426871929ed7b46215638088270d9b34/pillow-12.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98edb152429ab62a1818039744d8fbb3ccab98a7c29fc3d5fcef158f3f1f68b7", size = 8074769, upload-time = "2026-02-11T04:22:35.877Z" }, + { url = "https://files.pythonhosted.org/packages/c3/bd/f1d71eb39a72fa088d938655afba3e00b38018d052752f435838961127d8/pillow-12.1.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d470ab1178551dd17fdba0fef463359c41aaa613cdcd7ff8373f54be629f9f8f", size = 6381358, upload-time = "2026-02-11T04:22:37.698Z" }, + { url = "https://files.pythonhosted.org/packages/64/ef/c784e20b96674ed36a5af839305f55616f8b4f8aa8eeccf8531a6e312243/pillow-12.1.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6408a7b064595afcab0a49393a413732a35788f2a5092fdc6266952ed67de586", size = 7068558, upload-time = "2026-02-11T04:22:39.597Z" }, + { url = "https://files.pythonhosted.org/packages/73/cb/8059688b74422ae61278202c4e1ad992e8a2e7375227be0a21c6b87ca8d5/pillow-12.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5d8c41325b382c07799a3682c1c258469ea2ff97103c53717b7893862d0c98ce", size = 6493028, upload-time = "2026-02-11T04:22:42.73Z" }, + { url = "https://files.pythonhosted.org/packages/c6/da/e3c008ed7d2dd1f905b15949325934510b9d1931e5df999bb15972756818/pillow-12.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7697918b5be27424e9ce568193efd13d925c4481dd364e43f5dff72d33e10f8", size = 7191940, upload-time = "2026-02-11T04:22:44.543Z" }, + { url = "https://files.pythonhosted.org/packages/01/4a/9202e8d11714c1fc5951f2e1ef362f2d7fbc595e1f6717971d5dd750e969/pillow-12.1.1-cp314-cp314t-win32.whl", hash = "sha256:d2912fd8114fc5545aa3a4b5576512f64c55a03f3ebcca4c10194d593d43ea36", size = 6438736, upload-time = "2026-02-11T04:22:46.347Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ca/cbce2327eb9885476b3957b2e82eb12c866a8b16ad77392864ad601022ce/pillow-12.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4ceb838d4bd9dab43e06c363cab2eebf63846d6a4aeaea283bbdfd8f1a8ed58b", size = 7182894, upload-time = "2026-02-11T04:22:48.114Z" }, + { url = "https://files.pythonhosted.org/packages/ec/d2/de599c95ba0a973b94410477f8bf0b6f0b5e67360eb89bcb1ad365258beb/pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334", size = 2546446, upload-time = "2026-02-11T04:22:50.342Z" }, ] [[package]] @@ -1394,29 +1423,29 @@ wheels = [ [[package]] name = "proto-plus" -version = "1.26.1" +version = "1.27.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142, upload-time = "2025-03-10T15:54:38.843Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/02/8832cde80e7380c600fbf55090b6ab7b62bd6825dbedde6d6657c15a1f8e/proto_plus-1.27.1.tar.gz", hash = "sha256:912a7460446625b792f6448bade9e55cd4e41e6ac10e27009ef71a7f317fa147", size = 56929, upload-time = "2026-02-02T17:34:49.035Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163, upload-time = "2025-03-10T15:54:37.335Z" }, + { url = "https://files.pythonhosted.org/packages/5d/79/ac273cbbf744691821a9cca88957257f41afe271637794975ca090b9588b/proto_plus-1.27.1-py3-none-any.whl", hash = "sha256:e4643061f3a4d0de092d62aa4ad09fa4756b2cbb89d4627f3985018216f9fefc", size = 50480, upload-time = "2026-02-02T17:34:47.339Z" }, ] [[package]] name = "protobuf" -version = "6.33.2" +version = "6.33.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/34/44/e49ecff446afeec9d1a66d6bbf9adc21e3c7cea7803a920ca3773379d4f6/protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4", size = 444296, upload-time = "2025-12-06T00:17:53.311Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465, upload-time = "2026-01-29T21:51:33.494Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/91/1e3a34881a88697a7354ffd177e8746e97a722e5e8db101544b47e84afb1/protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d", size = 425603, upload-time = "2025-12-06T00:17:41.114Z" }, - { url = "https://files.pythonhosted.org/packages/64/20/4d50191997e917ae13ad0a235c8b42d8c1ab9c3e6fd455ca16d416944355/protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4", size = 436930, upload-time = "2025-12-06T00:17:43.278Z" }, - { url = "https://files.pythonhosted.org/packages/b2/ca/7e485da88ba45c920fb3f50ae78de29ab925d9e54ef0de678306abfbb497/protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43", size = 427621, upload-time = "2025-12-06T00:17:44.445Z" }, - { url = "https://files.pythonhosted.org/packages/7d/4f/f743761e41d3b2b2566748eb76bbff2b43e14d5fcab694f494a16458b05f/protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e", size = 324460, upload-time = "2025-12-06T00:17:45.678Z" }, - { url = "https://files.pythonhosted.org/packages/b1/fa/26468d00a92824020f6f2090d827078c09c9c587e34cbfd2d0c7911221f8/protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872", size = 339168, upload-time = "2025-12-06T00:17:46.813Z" }, - { url = "https://files.pythonhosted.org/packages/56/13/333b8f421738f149d4fe5e49553bc2a2ab75235486259f689b4b91f96cec/protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f", size = 323270, upload-time = "2025-12-06T00:17:48.253Z" }, - { url = "https://files.pythonhosted.org/packages/0e/15/4f02896cc3df04fc465010a4c6a0cd89810f54617a32a70ef531ed75d61c/protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c", size = 170501, upload-time = "2025-12-06T00:17:52.211Z" }, + { url = "https://files.pythonhosted.org/packages/b1/79/af92d0a8369732b027e6d6084251dd8e782c685c72da161bd4a2e00fbabb/protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b", size = 425769, upload-time = "2026-01-29T21:51:21.751Z" }, + { url = "https://files.pythonhosted.org/packages/55/75/bb9bc917d10e9ee13dee8607eb9ab963b7cf8be607c46e7862c748aa2af7/protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c", size = 437118, upload-time = "2026-01-29T21:51:24.022Z" }, + { url = "https://files.pythonhosted.org/packages/a2/6b/e48dfc1191bc5b52950246275bf4089773e91cb5ba3592621723cdddca62/protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5", size = 427766, upload-time = "2026-01-29T21:51:25.413Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b1/c79468184310de09d75095ed1314b839eb2f72df71097db9d1404a1b2717/protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190", size = 324638, upload-time = "2026-01-29T21:51:26.423Z" }, + { url = "https://files.pythonhosted.org/packages/c5/f5/65d838092fd01c44d16037953fd4c2cc851e783de9b8f02b27ec4ffd906f/protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd", size = 339411, upload-time = "2026-01-29T21:51:27.446Z" }, + { url = "https://files.pythonhosted.org/packages/9b/53/a9443aa3ca9ba8724fdfa02dd1887c1bcd8e89556b715cfbacca6b63dbec/protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0", size = 323465, upload-time = "2026-01-29T21:51:28.925Z" }, + { url = "https://files.pythonhosted.org/packages/57/bf/2086963c69bdac3d7cff1cc7ff79b8ce5ea0bec6797a017e1be338a46248/protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02", size = 170687, upload-time = "2026-01-29T21:51:32.557Z" }, ] [[package]] @@ -1467,11 +1496,11 @@ wheels = [ [[package]] name = "pwdlib" -version = "0.2.1" +version = "0.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/a0/9daed437a6226f632a25d98d65d60ba02bdafa920c90dcb6454c611ead6c/pwdlib-0.2.1.tar.gz", hash = "sha256:9a1d8a8fa09a2f7ebf208265e55d7d008103cbdc82b9e4902ffdd1ade91add5e", size = 11699, upload-time = "2024-08-19T06:48:59.58Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/41/a7c0d8a003c36ce3828ae3ed0391fe6a15aad65f082dbd6bec817ea95c0b/pwdlib-0.3.0.tar.gz", hash = "sha256:6ca30f9642a1467d4f5d0a4d18619de1c77f17dfccb42dd200b144127d3c83fc", size = 215810, upload-time = "2025-10-25T12:44:24.395Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/f3/0dae5078a486f0fdf4d4a1121e103bc42694a9da9bea7b0f2c63f29cfbd3/pwdlib-0.2.1-py3-none-any.whl", hash = "sha256:1823dc6f22eae472b540e889ecf57fd424051d6a4023ec0bcf7f0de2d9d7ef8c", size = 8082, upload-time = "2024-08-19T06:49:00.997Z" }, + { url = "https://files.pythonhosted.org/packages/62/0c/9086a357d02a050fbb3270bf5043ac284dbfb845670e16c9389a41defc9e/pwdlib-0.3.0-py3-none-any.whl", hash = "sha256:f86c15c138858c09f3bba0a10984d4f9178158c55deaa72eac0210849b1a140d", size = 8633, upload-time = "2025-10-25T12:44:23.406Z" }, ] [package.optional-dependencies] @@ -1484,11 +1513,11 @@ bcrypt = [ [[package]] name = "pyasn1" -version = "0.6.1" +version = "0.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" }, ] [[package]] @@ -1505,11 +1534,11 @@ wheels = [ [[package]] name = "pycparser" -version = "2.23" +version = "3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } +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/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, + { 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]] @@ -1587,15 +1616,15 @@ wheels = [ [[package]] name = "pydantic-extra-types" -version = "2.10.6" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/10/fb64987804cde41bcc39d9cd757cd5f2bb5d97b389d81aa70238b14b8a7e/pydantic_extra_types-2.10.6.tar.gz", hash = "sha256:c63d70bf684366e6bbe1f4ee3957952ebe6973d41e7802aea0b770d06b116aeb", size = 141858, upload-time = "2025-10-08T13:47:49.483Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226, upload-time = "2025-12-31T16:18:27.944Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/04/5c918669096da8d1c9ec7bb716bd72e755526103a61bc5e76a3e4fb23b53/pydantic_extra_types-2.10.6-py3-none-any.whl", hash = "sha256:6106c448316d30abf721b5b9fecc65e983ef2614399a24142d689c7546cc246a", size = 40949, upload-time = "2025-10-08T13:47:48.268Z" }, + { url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296, upload-time = "2025-12-31T16:18:26.38Z" }, ] [[package]] @@ -1614,14 +1643,14 @@ wheels = [ [[package]] name = "pydot" -version = "3.0.4" +version = "4.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyparsing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/dd/e0e6a4fb84c22050f6a9701ad9fd6a67ef82faa7ba97b97eb6fdc6b49b34/pydot-3.0.4.tar.gz", hash = "sha256:3ce88b2558f3808b0376f22bfa6c263909e1c3981e2a7b629b65b451eee4a25d", size = 168167, upload-time = "2025-01-05T16:18:45.763Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/35/b17cb89ff865484c6a20ef46bf9d95a5f07328292578de0b295f4a6beec2/pydot-4.0.1.tar.gz", hash = "sha256:c2148f681c4a33e08bf0e26a9e5f8e4099a82e0e2a068098f32ce86577364ad5", size = 162594, upload-time = "2025-06-17T20:09:56.454Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/5f/1ebfd430df05c4f9e438dd3313c4456eab937d976f6ab8ce81a98f9fb381/pydot-3.0.4-py3-none-any.whl", hash = "sha256:bfa9c3fc0c44ba1d132adce131802d7df00429d1a79cc0346b0a5cd374dbe9c6", size = 35776, upload-time = "2025-01-05T16:18:42.836Z" }, + { url = "https://files.pythonhosted.org/packages/7e/32/a7125fb28c4261a627f999d5fb4afff25b523800faed2c30979949d6facd/pydot-4.0.1-py3-none-any.whl", hash = "sha256:869c0efadd2708c0be1f916eb669f3d664ca684bc57ffb7ecc08e70d5e93fee6", size = 37087, upload-time = "2025-06-17T20:09:55.25Z" }, ] [[package]] @@ -1635,11 +1664,11 @@ wheels = [ [[package]] name = "pyjwt" -version = "2.10.1" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, + { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, ] [package.optional-dependencies] @@ -1649,11 +1678,11 @@ crypto = [ [[package]] name = "pyparsing" -version = "3.2.5" +version = "3.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274, upload-time = "2025-09-21T04:11:06.277Z" } +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/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" }, + { 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]] @@ -1665,19 +1694,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" }, ] -[[package]] -name = "pyright" -version = "1.1.407" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nodeenv" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a6/1b/0aa08ee42948b61745ac5b5b5ccaec4669e8884b53d31c8ec20b2fcd6b6f/pyright-1.1.407.tar.gz", hash = "sha256:099674dba5c10489832d4a4b2d302636152a9a42d317986c38474c76fe562262", size = 4122872, upload-time = "2025-10-24T23:17:15.145Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/93/b69052907d032b00c40cb656d21438ec00b3a471733de137a3f65a49a0a0/pyright-1.1.407-py3-none-any.whl", hash = "sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21", size = 5997008, upload-time = "2025-10-24T23:17:13.159Z" }, -] - [[package]] name = "pytest" version = "9.0.2" @@ -1757,11 +1773,11 @@ wheels = [ [[package]] name = "python-multipart" -version = "0.0.20" +version = "0.0.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, ] [[package]] @@ -1776,15 +1792,6 @@ 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 = "pytz" -version = "2025.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, -] - [[package]] name = "pyyaml" version = "6.0.3" @@ -1823,11 +1830,11 @@ wheels = [ [[package]] name = "redis" -version = "7.1.0" +version = "7.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/80/2971931d27651affa88a44c0ad7b8c4a19dc29c998abb20b23868d319b59/redis-7.1.1.tar.gz", hash = "sha256:a2814b2bda15b39dad11391cc48edac4697214a8a5a4bd10abe936ab4892eb43", size = 4800064, upload-time = "2026-02-09T18:39:40.292Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, + { url = "https://files.pythonhosted.org/packages/29/55/1de1d812ba1481fa4b37fb03b4eec0fcb71b6a0d44c04ea3482eb017600f/redis-7.1.1-py3-none-any.whl", hash = "sha256:f77817f16071c2950492c67d40b771fa493eb3fccc630a424a10976dbb794b7a", size = 356057, upload-time = "2026-02-09T18:39:38.602Z" }, ] [[package]] @@ -1937,8 +1944,8 @@ api = [ dev = [ { name = "alembic-autogen-check" }, { name = "paracelsus" }, - { name = "pyright" }, { name = "ruff" }, + { name = "ty" }, ] migrations = [ { name = "alembic" }, @@ -1994,8 +2001,8 @@ api = [ dev = [ { name = "alembic-autogen-check", specifier = ">=1.1.1" }, { name = "paracelsus", specifier = ">=0.9.0" }, - { name = "pyright", specifier = ">=1.1.402" }, { name = "ruff", specifier = ">=0.12.1" }, + { name = "ty", specifier = ">=0.0.15" }, ] migrations = [ { name = "alembic", specifier = ">=1.16.2" }, @@ -2053,29 +2060,29 @@ wheels = [ [[package]] name = "rich" -version = "14.2.0" +version = "14.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, + { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" }, ] [[package]] name = "rich-toolkit" -version = "0.17.0" +version = "0.19.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ad/d0/8f8de36e1abf8339b497ce700dd7251ca465ffca4a1976969b0eaeb596fb/rich_toolkit-0.17.0.tar.gz", hash = "sha256:17ca7a32e613001aa0945ddea27a246f6de01dfc4c12403254c057a8ee542977", size = 187955, upload-time = "2025-11-27T11:10:24.863Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/c9/4bbf4bfee195ed1b7d7a6733cc523ca61dbfb4a3e3c12ea090aaffd97597/rich_toolkit-0.19.4.tar.gz", hash = "sha256:52e23d56f9dc30d1343eb3b3f6f18764c313fbfea24e52e6a1d6069bec9c18eb", size = 193951, upload-time = "2026-02-12T10:08:15.814Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/42/ef2ed40699567661d03b0b511ac46cf6cee736de8f3666819c12d6d20696/rich_toolkit-0.17.0-py3-none-any.whl", hash = "sha256:06fb47a5c5259d6b480287cd38aff5f551b6e1a307f90ed592453dd360e4e71e", size = 31412, upload-time = "2025-11-27T11:10:23.847Z" }, + { url = "https://files.pythonhosted.org/packages/28/31/97d39719def09c134385bfcfbedfed255168b571e7beb3ad7765aae660ca/rich_toolkit-0.19.4-py3-none-any.whl", hash = "sha256:34ac344de8862801644be8b703e26becf44b047e687f208d7829e8f7cfc311d6", size = 32757, upload-time = "2026-02-12T10:08:15.037Z" }, ] [[package]] @@ -2145,28 +2152,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.14.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ed/d9/f7a0c4b3a2bf2556cd5d99b05372c29980249ef71e8e32669ba77428c82c/ruff-0.14.8.tar.gz", hash = "sha256:774ed0dd87d6ce925e3b8496feb3a00ac564bea52b9feb551ecd17e0a23d1eed", size = 5765385, upload-time = "2025-12-04T15:06:17.669Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/48/b8/9537b52010134b1d2b72870cc3f92d5fb759394094741b09ceccae183fbe/ruff-0.14.8-py3-none-linux_armv6l.whl", hash = "sha256:ec071e9c82eca417f6111fd39f7043acb53cd3fde9b1f95bbed745962e345afb", size = 13441540, upload-time = "2025-12-04T15:06:14.896Z" }, - { url = "https://files.pythonhosted.org/packages/24/00/99031684efb025829713682012b6dd37279b1f695ed1b01725f85fd94b38/ruff-0.14.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8cdb162a7159f4ca36ce980a18c43d8f036966e7f73f866ac8f493b75e0c27e9", size = 13669384, upload-time = "2025-12-04T15:06:51.809Z" }, - { url = "https://files.pythonhosted.org/packages/72/64/3eb5949169fc19c50c04f28ece2c189d3b6edd57e5b533649dae6ca484fe/ruff-0.14.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2e2fcbefe91f9fad0916850edf0854530c15bd1926b6b779de47e9ab619ea38f", size = 12806917, upload-time = "2025-12-04T15:06:08.925Z" }, - { url = "https://files.pythonhosted.org/packages/c4/08/5250babb0b1b11910f470370ec0cbc67470231f7cdc033cee57d4976f941/ruff-0.14.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9d70721066a296f45786ec31916dc287b44040f553da21564de0ab4d45a869b", size = 13256112, upload-time = "2025-12-04T15:06:23.498Z" }, - { url = "https://files.pythonhosted.org/packages/78/4c/6c588e97a8e8c2d4b522c31a579e1df2b4d003eddfbe23d1f262b1a431ff/ruff-0.14.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c87e09b3cd9d126fc67a9ecd3b5b1d3ded2b9c7fce3f16e315346b9d05cfb52", size = 13227559, upload-time = "2025-12-04T15:06:33.432Z" }, - { url = "https://files.pythonhosted.org/packages/23/ce/5f78cea13eda8eceac71b5f6fa6e9223df9b87bb2c1891c166d1f0dce9f1/ruff-0.14.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d62cb310c4fbcb9ee4ac023fe17f984ae1e12b8a4a02e3d21489f9a2a5f730c", size = 13896379, upload-time = "2025-12-04T15:06:02.687Z" }, - { url = "https://files.pythonhosted.org/packages/cf/79/13de4517c4dadce9218a20035b21212a4c180e009507731f0d3b3f5df85a/ruff-0.14.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1af35c2d62633d4da0521178e8a2641c636d2a7153da0bac1b30cfd4ccd91344", size = 15372786, upload-time = "2025-12-04T15:06:29.828Z" }, - { url = "https://files.pythonhosted.org/packages/00/06/33df72b3bb42be8a1c3815fd4fae83fa2945fc725a25d87ba3e42d1cc108/ruff-0.14.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:25add4575ffecc53d60eed3f24b1e934493631b48ebbc6ebaf9d8517924aca4b", size = 14990029, upload-time = "2025-12-04T15:06:36.812Z" }, - { url = "https://files.pythonhosted.org/packages/64/61/0f34927bd90925880394de0e081ce1afab66d7b3525336f5771dcf0cb46c/ruff-0.14.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c943d847b7f02f7db4201a0600ea7d244d8a404fbb639b439e987edcf2baf9a", size = 14407037, upload-time = "2025-12-04T15:06:39.979Z" }, - { url = "https://files.pythonhosted.org/packages/96/bc/058fe0aefc0fbf0d19614cb6d1a3e2c048f7dc77ca64957f33b12cfdc5ef/ruff-0.14.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb6e8bf7b4f627548daa1b69283dac5a296bfe9ce856703b03130732e20ddfe2", size = 14102390, upload-time = "2025-12-04T15:06:46.372Z" }, - { url = "https://files.pythonhosted.org/packages/af/a4/e4f77b02b804546f4c17e8b37a524c27012dd6ff05855d2243b49a7d3cb9/ruff-0.14.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7aaf2974f378e6b01d1e257c6948207aec6a9b5ba53fab23d0182efb887a0e4a", size = 14230793, upload-time = "2025-12-04T15:06:20.497Z" }, - { url = "https://files.pythonhosted.org/packages/3f/52/bb8c02373f79552e8d087cedaffad76b8892033d2876c2498a2582f09dcf/ruff-0.14.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e5758ca513c43ad8a4ef13f0f081f80f08008f410790f3611a21a92421ab045b", size = 13160039, upload-time = "2025-12-04T15:06:49.06Z" }, - { url = "https://files.pythonhosted.org/packages/1f/ad/b69d6962e477842e25c0b11622548df746290cc6d76f9e0f4ed7456c2c31/ruff-0.14.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f74f7ba163b6e85a8d81a590363bf71618847e5078d90827749bfda1d88c9cdf", size = 13205158, upload-time = "2025-12-04T15:06:54.574Z" }, - { url = "https://files.pythonhosted.org/packages/06/63/54f23da1315c0b3dfc1bc03fbc34e10378918a20c0b0f086418734e57e74/ruff-0.14.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eed28f6fafcc9591994c42254f5a5c5ca40e69a30721d2ab18bb0bb3baac3ab6", size = 13469550, upload-time = "2025-12-04T15:05:59.209Z" }, - { url = "https://files.pythonhosted.org/packages/70/7d/a4d7b1961e4903bc37fffb7ddcfaa7beb250f67d97cfd1ee1d5cddb1ec90/ruff-0.14.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:21d48fa744c9d1cb8d71eb0a740c4dd02751a5de9db9a730a8ef75ca34cf138e", size = 14211332, upload-time = "2025-12-04T15:06:06.027Z" }, - { url = "https://files.pythonhosted.org/packages/5d/93/2a5063341fa17054e5c86582136e9895db773e3c2ffb770dde50a09f35f0/ruff-0.14.8-py3-none-win32.whl", hash = "sha256:15f04cb45c051159baebb0f0037f404f1dc2f15a927418f29730f411a79bc4e7", size = 13151890, upload-time = "2025-12-04T15:06:11.668Z" }, - { url = "https://files.pythonhosted.org/packages/02/1c/65c61a0859c0add13a3e1cbb6024b42de587456a43006ca2d4fd3d1618fe/ruff-0.14.8-py3-none-win_amd64.whl", hash = "sha256:9eeb0b24242b5bbff3011409a739929f497f3fb5fe3b5698aba5e77e8c833097", size = 14537826, upload-time = "2025-12-04T15:06:26.409Z" }, - { url = "https://files.pythonhosted.org/packages/6d/63/8b41cea3afd7f58eb64ac9251668ee0073789a3bc9ac6f816c8c6fef986d/ruff-0.14.8-py3-none-win_arm64.whl", hash = "sha256:965a582c93c63fe715fd3e3f8aa37c4b776777203d8e1d8aa3cc0c14424a4b99", size = 13634522, upload-time = "2025-12-04T15:06:43.212Z" }, +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/39/5cee96809fbca590abea6b46c6d1c586b49663d1d2830a751cc8fc42c666/ruff-0.15.0.tar.gz", hash = "sha256:6bdea47cdbea30d40f8f8d7d69c0854ba7c15420ec75a26f463290949d7f7e9a", size = 4524893, upload-time = "2026-02-03T17:53:35.357Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/88/3fd1b0aa4b6330d6aaa63a285bc96c9f71970351579152d231ed90914586/ruff-0.15.0-py3-none-linux_armv6l.whl", hash = "sha256:aac4ebaa612a82b23d45964586f24ae9bc23ca101919f5590bdb368d74ad5455", size = 10354332, upload-time = "2026-02-03T17:52:54.892Z" }, + { url = "https://files.pythonhosted.org/packages/72/f6/62e173fbb7eb75cc29fe2576a1e20f0a46f671a2587b5f604bfb0eaf5f6f/ruff-0.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dcd4be7cc75cfbbca24a98d04d0b9b36a270d0833241f776b788d59f4142b14d", size = 10767189, upload-time = "2026-02-03T17:53:19.778Z" }, + { url = "https://files.pythonhosted.org/packages/99/e4/968ae17b676d1d2ff101d56dc69cf333e3a4c985e1ec23803df84fc7bf9e/ruff-0.15.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d747e3319b2bce179c7c1eaad3d884dc0a199b5f4d5187620530adf9105268ce", size = 10075384, upload-time = "2026-02-03T17:53:29.241Z" }, + { url = "https://files.pythonhosted.org/packages/a2/bf/9843c6044ab9e20af879c751487e61333ca79a2c8c3058b15722386b8cae/ruff-0.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:650bd9c56ae03102c51a5e4b554d74d825ff3abe4db22b90fd32d816c2e90621", size = 10481363, upload-time = "2026-02-03T17:52:43.332Z" }, + { url = "https://files.pythonhosted.org/packages/55/d9/4ada5ccf4cd1f532db1c8d44b6f664f2208d3d93acbeec18f82315e15193/ruff-0.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6664b7eac559e3048223a2da77769c2f92b43a6dfd4720cef42654299a599c9", size = 10187736, upload-time = "2026-02-03T17:53:00.522Z" }, + { url = "https://files.pythonhosted.org/packages/86/e2/f25eaecd446af7bb132af0a1d5b135a62971a41f5366ff41d06d25e77a91/ruff-0.15.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f811f97b0f092b35320d1556f3353bf238763420ade5d9e62ebd2b73f2ff179", size = 10968415, upload-time = "2026-02-03T17:53:15.705Z" }, + { url = "https://files.pythonhosted.org/packages/e7/dc/f06a8558d06333bf79b497d29a50c3a673d9251214e0d7ec78f90b30aa79/ruff-0.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761ec0a66680fab6454236635a39abaf14198818c8cdf691e036f4bc0f406b2d", size = 11809643, upload-time = "2026-02-03T17:53:23.031Z" }, + { url = "https://files.pythonhosted.org/packages/dd/45/0ece8db2c474ad7df13af3a6d50f76e22a09d078af63078f005057ca59eb/ruff-0.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940f11c2604d317e797b289f4f9f3fa5555ffe4fb574b55ed006c3d9b6f0eb78", size = 11234787, upload-time = "2026-02-03T17:52:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/8a/d9/0e3a81467a120fd265658d127db648e4d3acfe3e4f6f5d4ea79fac47e587/ruff-0.15.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbca3d40558789126da91d7ef9a7c87772ee107033db7191edefa34e2c7f1b4", size = 11112797, upload-time = "2026-02-03T17:52:49.274Z" }, + { url = "https://files.pythonhosted.org/packages/b2/cb/8c0b3b0c692683f8ff31351dfb6241047fa873a4481a76df4335a8bff716/ruff-0.15.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9a121a96db1d75fa3eb39c4539e607f628920dd72ff1f7c5ee4f1b768ac62d6e", size = 11033133, upload-time = "2026-02-03T17:53:33.105Z" }, + { url = "https://files.pythonhosted.org/packages/f8/5e/23b87370cf0f9081a8c89a753e69a4e8778805b8802ccfe175cc410e50b9/ruff-0.15.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5298d518e493061f2eabd4abd067c7e4fb89e2f63291c94332e35631c07c3662", size = 10442646, upload-time = "2026-02-03T17:53:06.278Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9a/3c94de5ce642830167e6d00b5c75aacd73e6347b4c7fc6828699b150a5ee/ruff-0.15.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afb6e603d6375ff0d6b0cee563fa21ab570fd15e65c852cb24922cef25050cf1", size = 10195750, upload-time = "2026-02-03T17:53:26.084Z" }, + { url = "https://files.pythonhosted.org/packages/30/15/e396325080d600b436acc970848d69df9c13977942fb62bb8722d729bee8/ruff-0.15.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77e515f6b15f828b94dc17d2b4ace334c9ddb7d9468c54b2f9ed2b9c1593ef16", size = 10676120, upload-time = "2026-02-03T17:53:09.363Z" }, + { url = "https://files.pythonhosted.org/packages/8d/c9/229a23d52a2983de1ad0fb0ee37d36e0257e6f28bfd6b498ee2c76361874/ruff-0.15.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6f6e80850a01eb13b3e42ee0ebdf6e4497151b48c35051aab51c101266d187a3", size = 11201636, upload-time = "2026-02-03T17:52:57.281Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b0/69adf22f4e24f3677208adb715c578266842e6e6a3cc77483f48dd999ede/ruff-0.15.0-py3-none-win32.whl", hash = "sha256:238a717ef803e501b6d51e0bdd0d2c6e8513fe9eec14002445134d3907cd46c3", size = 10465945, upload-time = "2026-02-03T17:53:12.591Z" }, + { url = "https://files.pythonhosted.org/packages/51/ad/f813b6e2c97e9b4598be25e94a9147b9af7e60523b0cb5d94d307c15229d/ruff-0.15.0-py3-none-win_amd64.whl", hash = "sha256:dd5e4d3301dc01de614da3cdffc33d4b1b96fb89e45721f1598e5532ccf78b18", size = 11564657, upload-time = "2026-02-03T17:52:51.893Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b0/2d823f6e77ebe560f4e397d078487e8d52c1516b331e3521bc75db4272ca/ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a", size = 10865753, upload-time = "2026-02-03T17:53:03.014Z" }, ] [[package]] @@ -2183,15 +2189,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.47.0" +version = "2.52.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4a/2a/d225cbf87b6c8ecce5664db7bcecb82c317e448e3b24a2dcdaacb18ca9a7/sentry_sdk-2.47.0.tar.gz", hash = "sha256:8218891d5e41b4ea8d61d2aed62ed10c80e39d9f2959d6f939efbf056857e050", size = 381895, upload-time = "2025-12-03T14:06:36.846Z" } +sdist = { url = "https://files.pythonhosted.org/packages/59/eb/1b497650eb564701f9a7b8a95c51b2abe9347ed2c0b290ba78f027ebe4ea/sentry_sdk-2.52.0.tar.gz", hash = "sha256:fa0bec872cfec0302970b2996825723d67390cdd5f0229fb9efed93bd5384899", size = 410273, upload-time = "2026-02-04T15:03:54.706Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/ac/d6286ea0d49e7b58847faf67b00e56bb4ba3d525281e2ac306e1f1f353da/sentry_sdk-2.47.0-py2.py3-none-any.whl", hash = "sha256:d72f8c61025b7d1d9e52510d03a6247b280094a327dd900d987717a4fce93412", size = 411088, upload-time = "2025-12-03T14:06:35.374Z" }, + { url = "https://files.pythonhosted.org/packages/ca/63/2c6daf59d86b1c30600bff679d039f57fd1932af82c43c0bde1cbc55e8d4/sentry_sdk-2.52.0-py2.py3-none-any.whl", hash = "sha256:931c8f86169fc6f2752cb5c4e6480f0d516112e78750c312e081ababecbaf2ed", size = 435547, upload-time = "2026-02-04T15:03:51.567Z" }, ] [[package]] @@ -2214,32 +2220,46 @@ wheels = [ [[package]] name = "soupsieve" -version = "2.8" +version = "2.8.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627, upload-time = "2026-01-20T04:27:02.457Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" }, + { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" }, ] [[package]] name = "sqlalchemy" -version = "2.0.44" +version = "2.0.46" 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/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830, upload-time = "2025-10-10T14:39:12.935Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/45/d3/c67077a2249fdb455246e6853166360054c331db4613cda3e31ab1cadbef/sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1", size = 2135479, upload-time = "2025-10-10T16:03:37.671Z" }, - { url = "https://files.pythonhosted.org/packages/2b/91/eabd0688330d6fd114f5f12c4f89b0d02929f525e6bf7ff80aa17ca802af/sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45", size = 2123212, upload-time = "2025-10-10T16:03:41.755Z" }, - { url = "https://files.pythonhosted.org/packages/b0/bb/43e246cfe0e81c018076a16036d9b548c4cc649de241fa27d8d9ca6f85ab/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976", size = 3255353, upload-time = "2025-10-10T15:35:31.221Z" }, - { url = "https://files.pythonhosted.org/packages/b9/96/c6105ed9a880abe346b64d3b6ddef269ddfcab04f7f3d90a0bf3c5a88e82/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c", size = 3260222, upload-time = "2025-10-10T15:43:50.124Z" }, - { url = "https://files.pythonhosted.org/packages/44/16/1857e35a47155b5ad927272fee81ae49d398959cb749edca6eaa399b582f/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d", size = 3189614, upload-time = "2025-10-10T15:35:32.578Z" }, - { url = "https://files.pythonhosted.org/packages/88/ee/4afb39a8ee4fc786e2d716c20ab87b5b1fb33d4ac4129a1aaa574ae8a585/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40", size = 3226248, upload-time = "2025-10-10T15:43:51.862Z" }, - { url = "https://files.pythonhosted.org/packages/32/d5/0e66097fc64fa266f29a7963296b40a80d6a997b7ac13806183700676f86/sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73", size = 2101275, upload-time = "2025-10-10T15:03:26.096Z" }, - { url = "https://files.pythonhosted.org/packages/03/51/665617fe4f8c6450f42a6d8d69243f9420f5677395572c2fe9d21b493b7b/sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e", size = 2127901, upload-time = "2025-10-10T15:03:27.548Z" }, - { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718, upload-time = "2025-10-10T15:29:45.32Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/06/aa/9ce0f3e7a9829ead5c8ce549392f33a12c4555a6c0609bb27d882e9c7ddf/sqlalchemy-2.0.46.tar.gz", hash = "sha256:cf36851ee7219c170bb0793dbc3da3e80c582e04a5437bc601bfe8c85c9216d7", size = 9865393, upload-time = "2026-01-21T18:03:45.119Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/4b/fa7838fe20bb752810feed60e45625a9a8b0102c0c09971e2d1d95362992/sqlalchemy-2.0.46-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:93a12da97cca70cea10d4b4fc602589c4511f96c1f8f6c11817620c021d21d00", size = 2150268, upload-time = "2026-01-21T19:05:56.621Z" }, + { url = "https://files.pythonhosted.org/packages/46/c1/b34dccd712e8ea846edf396e00973dda82d598cb93762e55e43e6835eba9/sqlalchemy-2.0.46-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af865c18752d416798dae13f83f38927c52f085c52e2f32b8ab0fef46fdd02c2", size = 3276511, upload-time = "2026-01-21T18:46:49.022Z" }, + { url = "https://files.pythonhosted.org/packages/96/48/a04d9c94753e5d5d096c628c82a98c4793b9c08ca0e7155c3eb7d7db9f24/sqlalchemy-2.0.46-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8d679b5f318423eacb61f933a9a0f75535bfca7056daeadbf6bd5bcee6183aee", size = 3292881, upload-time = "2026-01-21T18:40:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/be/f4/06eda6e91476f90a7d8058f74311cb65a2fb68d988171aced81707189131/sqlalchemy-2.0.46-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64901e08c33462acc9ec3bad27fc7a5c2b6491665f2aa57564e57a4f5d7c52ad", size = 3224559, upload-time = "2026-01-21T18:46:50.974Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a2/d2af04095412ca6345ac22b33b89fe8d6f32a481e613ffcb2377d931d8d0/sqlalchemy-2.0.46-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8ac45e8f4eaac0f9f8043ea0e224158855c6a4329fd4ee37c45c61e3beb518e", size = 3262728, upload-time = "2026-01-21T18:40:14.883Z" }, + { url = "https://files.pythonhosted.org/packages/31/48/1980c7caa5978a3b8225b4d230e69a2a6538a3562b8b31cea679b6933c83/sqlalchemy-2.0.46-cp313-cp313-win32.whl", hash = "sha256:8d3b44b3d0ab2f1319d71d9863d76eeb46766f8cf9e921ac293511804d39813f", size = 2111295, upload-time = "2026-01-21T18:42:52.366Z" }, + { url = "https://files.pythonhosted.org/packages/2d/54/f8d65bbde3d877617c4720f3c9f60e99bb7266df0d5d78b6e25e7c149f35/sqlalchemy-2.0.46-cp313-cp313-win_amd64.whl", hash = "sha256:77f8071d8fbcbb2dd11b7fd40dedd04e8ebe2eb80497916efedba844298065ef", size = 2137076, upload-time = "2026-01-21T18:42:53.924Z" }, + { url = "https://files.pythonhosted.org/packages/56/ba/9be4f97c7eb2b9d5544f2624adfc2853e796ed51d2bb8aec90bc94b7137e/sqlalchemy-2.0.46-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1e8cc6cc01da346dc92d9509a63033b9b1bda4fed7a7a7807ed385c7dccdc10", size = 3556533, upload-time = "2026-01-21T18:33:06.636Z" }, + { url = "https://files.pythonhosted.org/packages/20/a6/b1fc6634564dbb4415b7ed6419cdfeaadefd2c39cdab1e3aa07a5f2474c2/sqlalchemy-2.0.46-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:96c7cca1a4babaaf3bfff3e4e606e38578856917e52f0384635a95b226c87764", size = 3523208, upload-time = "2026-01-21T18:45:08.436Z" }, + { url = "https://files.pythonhosted.org/packages/a1/d8/41e0bdfc0f930ff236f86fccd12962d8fa03713f17ed57332d38af6a3782/sqlalchemy-2.0.46-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b2a9f9aee38039cf4755891a1e50e1effcc42ea6ba053743f452c372c3152b1b", size = 3464292, upload-time = "2026-01-21T18:33:08.208Z" }, + { url = "https://files.pythonhosted.org/packages/f0/8b/9dcbec62d95bea85f5ecad9b8d65b78cc30fb0ffceeb3597961f3712549b/sqlalchemy-2.0.46-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:db23b1bf8cfe1f7fda19018e7207b20cdb5168f83c437ff7e95d19e39289c447", size = 3473497, upload-time = "2026-01-21T18:45:10.552Z" }, + { url = "https://files.pythonhosted.org/packages/e9/f8/5ecdfc73383ec496de038ed1614de9e740a82db9ad67e6e4514ebc0708a3/sqlalchemy-2.0.46-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:56bdd261bfd0895452006d5316cbf35739c53b9bb71a170a331fa0ea560b2ada", size = 2152079, upload-time = "2026-01-21T19:05:58.477Z" }, + { url = "https://files.pythonhosted.org/packages/e5/bf/eba3036be7663ce4d9c050bc3d63794dc29fbe01691f2bf5ccb64e048d20/sqlalchemy-2.0.46-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33e462154edb9493f6c3ad2125931e273bbd0be8ae53f3ecd1c161ea9a1dd366", size = 3272216, upload-time = "2026-01-21T18:46:52.634Z" }, + { url = "https://files.pythonhosted.org/packages/05/45/1256fb597bb83b58a01ddb600c59fe6fdf0e5afe333f0456ed75c0f8d7bd/sqlalchemy-2.0.46-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bcdce05f056622a632f1d44bb47dbdb677f58cad393612280406ce37530eb6d", size = 3277208, upload-time = "2026-01-21T18:40:16.38Z" }, + { url = "https://files.pythonhosted.org/packages/d9/a0/2053b39e4e63b5d7ceb3372cface0859a067c1ddbd575ea7e9985716f771/sqlalchemy-2.0.46-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e84b09a9b0f19accedcbeff5c2caf36e0dd537341a33aad8d680336152dc34e", size = 3221994, upload-time = "2026-01-21T18:46:54.622Z" }, + { url = "https://files.pythonhosted.org/packages/1e/87/97713497d9502553c68f105a1cb62786ba1ee91dea3852ae4067ed956a50/sqlalchemy-2.0.46-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4f52f7291a92381e9b4de9050b0a65ce5d6a763333406861e33906b8aa4906bf", size = 3243990, upload-time = "2026-01-21T18:40:18.253Z" }, + { url = "https://files.pythonhosted.org/packages/a8/87/5d1b23548f420ff823c236f8bea36b1a997250fd2f892e44a3838ca424f4/sqlalchemy-2.0.46-cp314-cp314-win32.whl", hash = "sha256:70ed2830b169a9960193f4d4322d22be5c0925357d82cbf485b3369893350908", size = 2114215, upload-time = "2026-01-21T18:42:55.232Z" }, + { url = "https://files.pythonhosted.org/packages/3a/20/555f39cbcf0c10cf452988b6a93c2a12495035f68b3dbd1a408531049d31/sqlalchemy-2.0.46-cp314-cp314-win_amd64.whl", hash = "sha256:3c32e993bc57be6d177f7d5d31edb93f30726d798ad86ff9066d75d9bf2e0b6b", size = 2139867, upload-time = "2026-01-21T18:42:56.474Z" }, + { url = "https://files.pythonhosted.org/packages/3e/f0/f96c8057c982d9d8a7a68f45d69c674bc6f78cad401099692fe16521640a/sqlalchemy-2.0.46-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4dafb537740eef640c4d6a7c254611dca2df87eaf6d14d6a5fca9d1f4c3fc0fa", size = 3561202, upload-time = "2026-01-21T18:33:10.337Z" }, + { url = "https://files.pythonhosted.org/packages/d7/53/3b37dda0a5b137f21ef608d8dfc77b08477bab0fe2ac9d3e0a66eaeab6fc/sqlalchemy-2.0.46-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42a1643dc5427b69aca967dae540a90b0fbf57eaf248f13a90ea5930e0966863", size = 3526296, upload-time = "2026-01-21T18:45:12.657Z" }, + { url = "https://files.pythonhosted.org/packages/33/75/f28622ba6dde79cd545055ea7bd4062dc934e0621f7b3be2891f8563f8de/sqlalchemy-2.0.46-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ff33c6e6ad006bbc0f34f5faf941cfc62c45841c64c0a058ac38c799f15b5ede", size = 3470008, upload-time = "2026-01-21T18:33:11.725Z" }, + { url = "https://files.pythonhosted.org/packages/a9/42/4afecbbc38d5e99b18acef446453c76eec6fbd03db0a457a12a056836e22/sqlalchemy-2.0.46-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:82ec52100ec1e6ec671563bbd02d7c7c8d0b9e71a0723c72f22ecf52d1755330", size = 3476137, upload-time = "2026-01-21T18:45:15.001Z" }, + { url = "https://files.pythonhosted.org/packages/fc/a1/9c4efa03300926601c19c18582531b45aededfb961ab3c3585f1e24f120b/sqlalchemy-2.0.46-py3-none-any.whl", hash = "sha256:f9c11766e7e7c0a2767dda5acb006a118640c9fc0a4104214b96269bfb78399e", size = 1937882, upload-time = "2026-01-21T18:22:10.456Z" }, ] [package.optional-dependencies] @@ -2249,27 +2269,27 @@ asyncio = [ [[package]] name = "sqlmodel" -version = "0.0.27" +version = "0.0.33" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/5a/693d90866233e837d182da76082a6d4c2303f54d3aaaa5c78e1238c5d863/sqlmodel-0.0.27.tar.gz", hash = "sha256:ad1227f2014a03905aef32e21428640848ac09ff793047744a73dfdd077ff620", size = 118053, upload-time = "2025-10-08T16:39:11.938Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/62/22c287122598e61d07d005eec0b4eb97e6bde9a1b051bcd66c2bca846ea8/sqlmodel-0.0.33.tar.gz", hash = "sha256:b473544ed5fc2097894d89033049e569e1f138363dd3ec2ed4b6932cc9f29f5f", size = 95578, upload-time = "2026-02-11T15:23:39.504Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/92/c35e036151fe53822893979f8a13e6f235ae8191f4164a79ae60a95d66aa/sqlmodel-0.0.27-py3-none-any.whl", hash = "sha256:667fe10aa8ff5438134668228dc7d7a08306f4c5c4c7e6ad3ad68defa0e7aa49", size = 29131, upload-time = "2025-10-08T16:39:10.917Z" }, + { url = "https://files.pythonhosted.org/packages/63/39/13891bae4658133b489a4d8b6a2f193d56110e392289560f312748e796dc/sqlmodel-0.0.33-py3-none-any.whl", hash = "sha256:9045bb4d97d2ba099c5a068ee9525af2d106972dda1ff8488e187ce50556bf73", size = 27444, upload-time = "2026-02-11T15:23:38.678Z" }, ] [[package]] name = "starlette" -version = "0.50.0" +version = "0.52.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, + { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, ] [[package]] @@ -2283,7 +2303,7 @@ wheels = [ [[package]] name = "tldextract" -version = "5.3.0" +version = "5.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -2291,24 +2311,48 @@ dependencies = [ { name = "requests" }, { name = "requests-file" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/78/182641ea38e3cfd56e9c7b3c0d48a53d432eea755003aa544af96403d4ac/tldextract-5.3.0.tar.gz", hash = "sha256:b3d2b70a1594a0ecfa6967d57251527d58e00bb5a91a74387baa0d87a0678609", size = 128502, upload-time = "2025-04-22T06:19:37.491Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/7b/644fbbb49564a6cb124a8582013315a41148dba2f72209bba14a84242bf0/tldextract-5.3.1.tar.gz", hash = "sha256:a72756ca170b2510315076383ea2993478f7da6f897eef1f4a5400735d5057fb", size = 126105, upload-time = "2025-12-28T23:58:05.532Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/42/0e49d6d0aac449ca71952ec5bae764af009754fcb2e76a5cc097543747b3/tldextract-5.3.1-py3-none-any.whl", hash = "sha256:6bfe36d518de569c572062b788e16a659ccaceffc486d243af0484e8ecf432d9", size = 105886, upload-time = "2025-12-28T23:58:04.071Z" }, +] + +[[package]] +name = "ty" +version = "0.0.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/18/77f84d89db54ea0d1d1b09fa2f630ac4c240c8e270761cb908c06b6e735c/ty-0.0.16.tar.gz", hash = "sha256:a999b0db6aed7d6294d036ebe43301105681e0c821a19989be7c145805d7351c", size = 5129637, upload-time = "2026-02-10T20:24:16.48Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/67/7c/ea488ef48f2f544566947ced88541bc45fae9e0e422b2edbf165ee07da99/tldextract-5.3.0-py3-none-any.whl", hash = "sha256:f70f31d10b55c83993f55e91ecb7c5d84532a8972f22ec578ecfbe5ea2292db2", size = 107384, upload-time = "2025-04-22T06:19:36.304Z" }, + { url = "https://files.pythonhosted.org/packages/67/b9/909ebcc7f59eaf8a2c18fb54bfcf1c106f99afb3e5460058d4b46dec7b20/ty-0.0.16-py3-none-linux_armv6l.whl", hash = "sha256:6d8833b86396ed742f2b34028f51c0e98dbf010b13ae4b79d1126749dc9dab15", size = 10113870, upload-time = "2026-02-10T20:24:11.864Z" }, + { url = "https://files.pythonhosted.org/packages/c3/2c/b963204f3df2fdbf46a4a1ea4a060af9bb676e065d59c70ad0f5ae0dbae8/ty-0.0.16-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:934c0055d3b7f1cf3c8eab78c6c127ef7f347ff00443cef69614bda6f1502377", size = 9936286, upload-time = "2026-02-10T20:24:08.695Z" }, + { url = "https://files.pythonhosted.org/packages/ef/4d/3d78294f2ddfdded231e94453dea0e0adef212b2bd6536296039164c2a3e/ty-0.0.16-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b55e8e8733b416d914003cd22e831e139f034681b05afed7e951cc1a5ea1b8d4", size = 9442660, upload-time = "2026-02-10T20:24:02.704Z" }, + { url = "https://files.pythonhosted.org/packages/15/40/ce48c0541e3b5749b0890725870769904e6b043e077d4710e5325d5cf807/ty-0.0.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feccae8f4abd6657de111353bd604f36e164844466346eb81ffee2c2b06ea0f0", size = 9934506, upload-time = "2026-02-10T20:24:35.818Z" }, + { url = "https://files.pythonhosted.org/packages/84/16/3b29de57e1ec6e56f50a4bb625ee0923edb058c5f53e29014873573a00cd/ty-0.0.16-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1cad5e29d8765b92db5fa284940ac57149561f3f89470b363b9aab8a6ce553b0", size = 9933099, upload-time = "2026-02-10T20:24:43.003Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a1/e546995c25563d318c502b2f42af0fdbed91e1fc343708241e2076373644/ty-0.0.16-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86f28797c7dc06f081238270b533bf4fc8e93852f34df49fb660e0b58a5cda9a", size = 10438370, upload-time = "2026-02-10T20:24:33.44Z" }, + { url = "https://files.pythonhosted.org/packages/11/c1/22d301a4b2cce0f75ae84d07a495f87da193bcb68e096d43695a815c4708/ty-0.0.16-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be971a3b42bcae44d0e5787f88156ed2102ad07558c05a5ae4bfd32a99118e66", size = 10992160, upload-time = "2026-02-10T20:24:25.574Z" }, + { url = "https://files.pythonhosted.org/packages/6f/40/f1892b8c890db3f39a1bab8ec459b572de2df49e76d3cad2a9a239adcde9/ty-0.0.16-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c9f982b7c4250eb91af66933f436b3a2363c24b6353e94992eab6551166c8b7", size = 10717892, upload-time = "2026-02-10T20:24:05.914Z" }, + { url = "https://files.pythonhosted.org/packages/2f/1b/caf9be8d0c738983845f503f2e92ea64b8d5fae1dd5ca98c3fca4aa7dadc/ty-0.0.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d122edf85ce7bdf6f85d19158c991d858fc835677bd31ca46319c4913043dc84", size = 10510916, upload-time = "2026-02-10T20:24:00.252Z" }, + { url = "https://files.pythonhosted.org/packages/60/ea/28980f5c7e1f4c9c44995811ea6a36f2fcb205232a6ae0f5b60b11504621/ty-0.0.16-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:497ebdddbb0e35c7758ded5aa4c6245e8696a69d531d5c9b0c1a28a075374241", size = 9908506, upload-time = "2026-02-10T20:24:28.133Z" }, + { url = "https://files.pythonhosted.org/packages/f7/80/8672306596349463c21644554f935ff8720679a14fd658fef658f66da944/ty-0.0.16-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e1e0ac0837bde634b030243aeba8499383c0487e08f22e80f5abdacb5b0bd8ce", size = 9949486, upload-time = "2026-02-10T20:24:18.62Z" }, + { url = "https://files.pythonhosted.org/packages/8b/8a/d8747d36f30bd82ea157835f5b70d084c9bb5d52dd9491dba8a149792d6a/ty-0.0.16-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1216c9bcca551d9f89f47a817ebc80e88ac37683d71504e5509a6445f24fd024", size = 10145269, upload-time = "2026-02-10T20:24:38.249Z" }, + { url = "https://files.pythonhosted.org/packages/6f/4c/753535acc7243570c259158b7df67e9c9dd7dab9a21ee110baa4cdcec45d/ty-0.0.16-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:221bbdd2c6ee558452c96916ab67fcc465b86967cf0482e19571d18f9c831828", size = 10608644, upload-time = "2026-02-10T20:24:40.565Z" }, + { url = "https://files.pythonhosted.org/packages/3e/05/8e8db64cf45a8b16757e907f7a3bfde8d6203e4769b11b64e28d5bdcd79a/ty-0.0.16-py3-none-win32.whl", hash = "sha256:d52c4eb786be878e7514cab637200af607216fcc5539a06d26573ea496b26512", size = 9582579, upload-time = "2026-02-10T20:24:30.406Z" }, + { url = "https://files.pythonhosted.org/packages/25/bc/45759faea132cd1b2a9ff8374e42ba03d39d076594fbb94f3e0e2c226c62/ty-0.0.16-py3-none-win_amd64.whl", hash = "sha256:f572c216aa8ecf79e86589c6e6d4bebc01f1f3cb3be765c0febd942013e1e73a", size = 10436043, upload-time = "2026-02-10T20:23:57.51Z" }, + { url = "https://files.pythonhosted.org/packages/7f/02/70a491802e7593e444137ed4e41a04c34d186eb2856f452dd76b60f2e325/ty-0.0.16-py3-none-win_arm64.whl", hash = "sha256:430eadeb1c0de0c31ef7bef9d002bdbb5f25a31e3aad546f1714d76cd8da0a87", size = 9915122, upload-time = "2026-02-10T20:24:14.285Z" }, ] [[package]] name = "typer" -version = "0.20.0" +version = "0.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "annotated-doc" }, { name = "click" }, { name = "rich" }, { name = "shellingham" }, - { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8f/28/7c85c8032b91dbe79725b6f17d2fffc595dff06a35c7a30a37bef73a1ab4/typer-0.20.0.tar.gz", hash = "sha256:1aaf6494031793e4876fb0bacfa6a912b551cf43c1e63c800df8b1a866720c37", size = 106492, upload-time = "2025-10-20T17:03:49.445Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/e6/44e073787aa57cd71c151f44855232feb0f748428fd5242d7366e3c4ae8b/typer-0.23.0.tar.gz", hash = "sha256:d8378833e47ada5d3d093fa20c4c63427cc4e27127f6b349a6c359463087d8cc", size = 120181, upload-time = "2026-02-11T15:22:18.637Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/64/7713ffe4b5983314e9d436a90d5bd4f63b6054e2aca783a3cfc44cb95bbf/typer-0.20.0-py3-none-any.whl", hash = "sha256:5b463df6793ec1dca6213a3cf4c0f03bc6e322ac5e16e13ddd622a889489784a", size = 47028, upload-time = "2025-10-20T17:03:47.617Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ed/d6fca788b51d0d4640c4bc82d0e85bad4b49809bca36bf4af01b4dcb66a7/typer-0.23.0-py3-none-any.whl", hash = "sha256:79f4bc262b6c37872091072a3cb7cb6d7d79ee98c0c658b4364bdcde3c42c913", size = 56668, upload-time = "2026-02-11T15:22:21.075Z" }, ] [[package]] @@ -2334,11 +2378,11 @@ wheels = [ [[package]] name = "tzdata" -version = "2025.2" +version = "2025.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, ] [[package]] @@ -2352,24 +2396,24 @@ wheels = [ [[package]] name = "urllib3" -version = "2.6.0" +version = "2.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/43/554c2569b62f49350597348fc3ac70f786e3c32e7f19d266e19817812dd3/urllib3-2.6.0.tar.gz", hash = "sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1", size = 432585, upload-time = "2025-12-05T15:08:47.885Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/1a/9ffe814d317c5224166b23e7c47f606d6e473712a2fad0f704ea9b99f246/urllib3-2.6.0-py3-none-any.whl", hash = "sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f", size = 131083, upload-time = "2025-12-05T15:08:45.983Z" }, + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] [[package]] name = "uvicorn" -version = "0.38.0" +version = "0.40.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605, upload-time = "2025-10-18T13:46:44.63Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109, upload-time = "2025-10-18T13:46:42.958Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, ] [package.optional-dependencies] @@ -2468,20 +2512,36 @@ wheels = [ [[package]] name = "websockets" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, - { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, - { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, - { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, - { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, - { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, - { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, +version = "16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, + { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, + { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, + { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, + { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, + { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, + { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, + { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, + { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, + { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, ] From 96204d08534b03e233184774f696095074d8c13a Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 12 Feb 2026 15:53:07 +0100 Subject: [PATCH 74/79] fix(backend): Update devcontainer and vscode settings for use of ty for type checking --- .devcontainer/backend/devcontainer.json | 2 +- .devcontainer/devcontainer.json | 1 + backend/.vscode/extensions.json | 2 +- backend/.vscode/settings.json | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.devcontainer/backend/devcontainer.json b/.devcontainer/backend/devcontainer.json index b6cd62a..e1c1edc 100644 --- a/.devcontainer/backend/devcontainer.json +++ b/.devcontainer/backend/devcontainer.json @@ -14,7 +14,7 @@ }, "customizations": { "vscode": { - "extensions": ["charliermarsh.ruff", "ms-python.python", "wholroyd.jinja"], + "extensions": ["astral-sh.ty", "charliermarsh.ruff", "ms-python.python", "wholroyd.jinja"], "settings": { "[python][notebook]": { "editor.codeActionsOnSave": { diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 162e29f..1ca703a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -20,6 +20,7 @@ "dbaeumer.vscode-eslint", "expo.vscode-expo-tools", // Backend + "astral-sh.ty", "charliermarsh.ruff", "ms-python.python", "wholroyd.jinja", diff --git a/backend/.vscode/extensions.json b/backend/.vscode/extensions.json index 5ab2c28..6bba2b4 100644 --- a/backend/.vscode/extensions.json +++ b/backend/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["charliermarsh.ruff", "ms-python.python", "wholroyd.jinja"] + "recommendations": ["astral-sh.ty", "charliermarsh.ruff", "ms-python.python", "wholroyd.jinja"] } diff --git a/backend/.vscode/settings.json b/backend/.vscode/settings.json index 57b9152..2eecfa4 100644 --- a/backend/.vscode/settings.json +++ b/backend/.vscode/settings.json @@ -13,5 +13,6 @@ "python.linting.ruffEnabled": true, "python.terminal.activateEnvInCurrentTerminal": true, "python.terminal.activateEnvironment": true, - "python.testing.pytestEnabled": true + "python.testing.pytestEnabled": true, + "ty.interpreter": ["${workspaceFolder}/.venv/bin/python"] } From 5a31886ad424acd35905d6182c922fcac840f5ed Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Thu, 12 Feb 2026 15:53:54 +0100 Subject: [PATCH 75/79] fix(backend): Some type issue fixes --- .pre-commit-config.yaml | 2 +- backend/app/api/auth/models.py | 8 +++---- backend/app/api/background_data/models.py | 2 +- .../app/api/background_data/routers/admin.py | 4 +++- backend/app/api/background_data/schemas.py | 4 ++-- backend/app/api/common/models/base.py | 21 ++++-------------- backend/app/api/common/models/custom_types.py | 3 +-- backend/app/api/data_collection/models.py | 2 +- backend/scripts/seed/taxonomies/common.py | 22 +++++++++++-------- backend/scripts/seed/taxonomies/cpv.py | 9 ++++---- .../seed/taxonomies/harmonized_system.py | 6 +++-- 11 files changed, 39 insertions(+), 44 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 35e8153..643f314 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -66,7 +66,7 @@ repos: args: ["--config", "backend/pyproject.toml"] - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.10.0 + rev: 0.10.2 hooks: - id: uv-lock # Update the uv lockfile for the backend. files: ^backend/(uv\.lock|pyproject\.toml|uv\.toml)$ diff --git a/backend/app/api/auth/models.py b/backend/app/api/auth/models.py index f412e12..6b21492 100644 --- a/backend/app/api/auth/models.py +++ b/backend/app/api/auth/models.py @@ -68,18 +68,18 @@ class User(SQLModelBaseUserDB, CustomBaseBare, UserBase, TimeStampMixinBare, tab nullable=True, ), ) - organization: Organization | None = Relationship( + organization: Optional["Organization"] = Relationship( # noqa: UP037, UP045 # `Optional` and quotes needed for proper sqlalchemy mapping back_populates="members", sa_relationship_kwargs={ "lazy": "selectin", - "primaryjoin": "User.organization_id == Organization.id", # HACK: Explicitly define join condition because of - "foreign_keys": "[User.organization_id]", # pydantic / sqlmodel issues + "primaryjoin": "User.organization_id == Organization.id", # HACK: Explicitly define join condition because + "foreign_keys": "[User.organization_id]", # of pydantic / sqlmodel issues }, ) organization_role: OrganizationRole | None = Field(default=None, sa_column=Column(SAEnum(OrganizationRole))) # One-to-one relationship with owned Organization - owned_organization: Organization | None = Relationship( + owned_organization: Optional["Organization"] = Relationship( # noqa: UP037, UP045 # `Optional` and quotes needed for proper sqlalchemy mapping back_populates="owner", sa_relationship_kwargs={ "uselist": False, diff --git a/backend/app/api/background_data/models.py b/backend/app/api/background_data/models.py index b9dcc79..4a3c22d 100644 --- a/backend/app/api/background_data/models.py +++ b/backend/app/api/background_data/models.py @@ -89,7 +89,7 @@ class Category(CategoryBase, TimeStampMixinBare, table=True): # Self-referential relationship supercategory_id: int | None = Field(foreign_key="category.id", default=None, nullable=True) - supercategory: Category | None = Relationship( + supercategory: Optional["Category"] = Relationship( # noqa: UP037, UP045 # `Optional` and quotes needed for proper sqlalchemy mapping back_populates="subcategories", sa_relationship_kwargs={"remote_side": "Category.id", "lazy": "selectin", "join_depth": 1}, ) diff --git a/backend/app/api/background_data/routers/admin.py b/backend/app/api/background_data/routers/admin.py index cdc4fa1..31b3e92 100644 --- a/backend/app/api/background_data/routers/admin.py +++ b/backend/app/api/background_data/routers/admin.py @@ -213,6 +213,7 @@ async def create_taxonomy( "description": "Taxonomy for materials", "domains": ["materials"], "source": "DOI:10.2345/12345", + "version": "1.0", }, }, "nested": { @@ -223,6 +224,7 @@ async def create_taxonomy( "description": "Taxonomy for materials", "domains": ["materials"], "source": "DOI:10.2345/12345", + "version": "1.0", "categories": [ { "name": "Metals", @@ -254,7 +256,7 @@ async def update_taxonomy( }, "advanced": { "summary": "Update domain and source", - "value": {"domain": "materials", "source": "https://new-source.com/taxonomy"}, + "value": {"domains": ["materials"], "source": "https://new-source.com/taxonomy"}, }, } ), diff --git a/backend/app/api/background_data/schemas.py b/backend/app/api/background_data/schemas.py index b594013..91c7f6b 100644 --- a/backend/app/api/background_data/schemas.py +++ b/backend/app/api/background_data/schemas.py @@ -207,7 +207,7 @@ class TaxonomyRead(BaseReadSchema, TaxonomyBase): { "name": "Materials Taxonomy", "description": "Taxonomy for materials", - "domain": "materials", + "domains": ["materials"], "source": "DOI:10.2345/12345", } ] @@ -230,7 +230,7 @@ class TaxonomyReadWithCategoryTree(TaxonomyRead): { "name": "Materials Taxonomy", "description": "Taxonomy for materials", - "domain": "materials", + "domains": ["materials"], "source": "DOI:10.2345/12345", "categories": [ { diff --git a/backend/app/api/common/models/base.py b/backend/app/api/common/models/base.py index 6ebc160..3d8bea9 100644 --- a/backend/app/api/common/models/base.py +++ b/backend/app/api/common/models/base.py @@ -4,13 +4,16 @@ from datetime import datetime from enum import Enum from functools import cached_property -from typing import Any, ClassVar, Self, TypeVar +from typing import TYPE_CHECKING, Any, ClassVar, Self, TypeVar from pydantic import BaseModel, ConfigDict, computed_field, model_validator from sqlalchemy import TIMESTAMP, func from sqlalchemy.dialects.postgresql import JSONB from sqlmodel import Column, Field, SQLModel +if TYPE_CHECKING: + from datetime import datetime + ### Base Model ### class APIModelName(BaseModel): @@ -102,15 +105,6 @@ def get_api_model_name(cls) -> APIModelName: class CustomBase(CustomBaseBare, SQLModel): """Base class for all models.""" - api_model_name: ClassVar[APIModelName | None] = None # The name of the model used in API routes - - @classmethod - def get_api_model_name(cls) -> APIModelName: - """Initialize api_model_name for the class.""" - if cls.api_model_name is None: - cls.api_model_name = APIModelName(name_camel=cls.__name__) - return cls.api_model_name - class CustomLinkingModelBase(CustomBase): """Base class for linking models.""" @@ -118,13 +112,6 @@ class CustomLinkingModelBase(CustomBase): # TODO: Separate schema and database model base classes. Schema models should inherit from Pydantic's BaseModel. # Database models should inherit from SQLModel. -class CustomDatabaseModelBase(CustomBase, SQLModel): - """Base class for models with database tables.""" - - id: int = Field( - default=None, - primary_key=True, - ) ### Mixins ### diff --git a/backend/app/api/common/models/custom_types.py b/backend/app/api/common/models/custom_types.py index 392c009..37a5575 100644 --- a/backend/app/api/common/models/custom_types.py +++ b/backend/app/api/common/models/custom_types.py @@ -10,8 +10,7 @@ ### Type aliases ### # Type alias for ID types -IDT = int | UUID - +IDT = TypeVar("IDT", bound=int | UUID) ### TypeVars ### # TypeVar for models MT = TypeVar("MT", bound=CustomBaseBare) diff --git a/backend/app/api/data_collection/models.py b/backend/app/api/data_collection/models.py index 6106d77..e626d21 100644 --- a/backend/app/api/data_collection/models.py +++ b/backend/app/api/data_collection/models.py @@ -124,7 +124,7 @@ class Product(ProductBase, TimeStampMixinBare, table=True): # Self-referential relationship for hierarchy parent_id: int | None = Field(default=None, foreign_key="product.id") - parent: Product | None = Relationship( + parent: Optional["Product"] = Relationship( # noqa: UP037, UP045 # `Optional` and quotes needed for proper sqlalchemy mapping back_populates="components", sa_relationship_kwargs={ "uselist": False, diff --git a/backend/scripts/seed/taxonomies/common.py b/backend/scripts/seed/taxonomies/common.py index 7e716fb..aadbf16 100644 --- a/backend/scripts/seed/taxonomies/common.py +++ b/backend/scripts/seed/taxonomies/common.py @@ -1,15 +1,16 @@ """Common utilities for seeding taxonomies and categories.""" import logging -from collections.abc import Callable -from typing import Any +from typing import TYPE_CHECKING, Any -from sqlalchemy import select -from sqlalchemy.orm import Session +from sqlmodel import select from app.api.background_data.models import Category, Taxonomy -logger = logging.getLogger("seeding.taxonomies") +if TYPE_CHECKING: + from collections.abc import Callable + + from sqlalchemy.orm import Session def configure_logging(level: int = logging.INFO) -> None: @@ -21,6 +22,9 @@ def configure_logging(level: int = logging.INFO) -> None: ) +logger = logging.getLogger("seeding.taxonomies.common") + + def get_or_create_taxonomy( session: Session, name: str, @@ -31,11 +35,12 @@ def get_or_create_taxonomy( ) -> Taxonomy: """Get existing taxonomy or create a new one.""" existing: Taxonomy | None = ( - session.execute(select(Taxonomy).where(Taxonomy.name == name, Taxonomy.version == version)).scalars().first() + session.execute(select(Taxonomy).where((Taxonomy.name == name) & (Taxonomy.version == version))) + .scalars() + .first() ) if existing: - logger.info("Taxonomy '%s' already exists (id: %s)", name, existing.id) return existing taxonomy = Taxonomy( @@ -61,14 +66,13 @@ def seed_categories_from_rows( Args: session: Database session - taxonomy: The taxonomy to add categories to + taxonomy: The taxonomy to add categories to (must be committed with non-None ID) rows: List of dictionaries with category data (must have 'external_id' and 'name') get_parent_id_fn: Function that takes a row and returns parent external_id or None Returns: Tuple of (categories_created, relationships_created) """ - # Build a map of external_id -> category for parent lookup id_to_category: dict[str, Category] = {} parent_relations: dict[str, str] = {} count = 0 diff --git a/backend/scripts/seed/taxonomies/cpv.py b/backend/scripts/seed/taxonomies/cpv.py index 13fc9ed..5afc655 100644 --- a/backend/scripts/seed/taxonomies/cpv.py +++ b/backend/scripts/seed/taxonomies/cpv.py @@ -10,9 +10,9 @@ import pandas as pd import requests -from sqlmodel import select +from sqlmodel import func, select -from app.api.auth.models import User # noqa: F401 # Need to explictly import User for SQLModel relationships +from app.api.auth.models import User # noqa: F401 # Need to explicitly import User for SQLModel relationships from app.api.background_data.models import ( Category, ProductType, # Adjust import as needed @@ -165,7 +165,8 @@ def seed_taxonomy(excel_path: Path = EXCEL_PATH) -> None: ) # If taxonomy already existed, skip seeding - existing_count = session.query(Category).filter_by(taxonomy_id=taxonomy.id).count() + existing_count = session.exec(select(func.count(Category.id)).where(Category.taxonomy_id == taxonomy.id)).one() + if existing_count > 0: logger.info("Taxonomy already has %d categories, skipping seeding", existing_count) return @@ -178,7 +179,7 @@ def seed_taxonomy(excel_path: Path = EXCEL_PATH) -> None: cat_count, rel_count = seed_categories_from_rows(session, taxonomy, rows, get_parent_id_fn=get_cpv_parent_id) # Commit - session.commit() + # session.commit() logger.info( "✓ Added %s taxonomy (version %s) with %d categories and %d relationships", TAXONOMY_NAME, diff --git a/backend/scripts/seed/taxonomies/harmonized_system.py b/backend/scripts/seed/taxonomies/harmonized_system.py index 7e96f31..e860b6b 100644 --- a/backend/scripts/seed/taxonomies/harmonized_system.py +++ b/backend/scripts/seed/taxonomies/harmonized_system.py @@ -6,9 +6,10 @@ from typing import Any import pandas as pd +from sqlmodel import func, select # TODO: Fix circular import issue with User model in seeding scripts -from app.api.auth.models import User # noqa: F401 # Need to explictly import User for SQLModel relationships +from app.api.auth.models import User # noqa: F401 # Need to explicitly import User for SQLModel relationships from app.api.background_data.models import Category, TaxonomyDomain from app.core.database import sync_session_context from scripts.seed.taxonomies.common import configure_logging, get_or_create_taxonomy, seed_categories_from_rows @@ -97,7 +98,8 @@ def seed_taxonomy() -> None: ) # If taxonomy already existed, skip seeding - existing_count = session.query(Category).filter_by(taxonomy_id=taxonomy.id).count() + existing_count = session.exec(select(func.count(Category.id)).where(Category.taxonomy_id == taxonomy.id)).one() + if existing_count > 0: logger.info("Taxonomy already has %d categories, skipping seeding", existing_count) return From 0f2a1313d6accdac1b1954ef5b21b2e85408d656 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 17 Feb 2026 15:38:28 +0100 Subject: [PATCH 76/79] fix(backend): Type fixes after ty linting - alembic migrations --- .../alembic/versions/33b00b31e537_initial.py | 85 ++++++++++--------- ...e309c_increase_string_fields_max_length.py | 36 ++++---- ...c94317b69_add_version_to_taxonomy_model.py | 2 +- ..._add_basic_circularity_properties_model.py | 18 ++-- 4 files changed, 71 insertions(+), 70 deletions(-) diff --git a/backend/alembic/versions/33b00b31e537_initial.py b/backend/alembic/versions/33b00b31e537_initial.py index e17d28c..d6d178a 100644 --- a/backend/alembic/versions/33b00b31e537_initial.py +++ b/backend/alembic/versions/33b00b31e537_initial.py @@ -14,6 +14,7 @@ from sqlalchemy.dialects import postgresql import app.api.common.models.custom_types +import app.api.file_storage.models.custom_types as file_storage_custom_types from alembic import op # revision identifiers, used by Alembic. @@ -34,9 +35,9 @@ def upgrade() -> None: "material", sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), - sa.Column("name", sqlmodel.sql.sqltypes.AutoString(length=50), nullable=False), - sa.Column("description", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True), - sa.Column("source", sqlmodel.sql.sqltypes.AutoString(length=50), nullable=True), + sa.Column("name", sqlmodel.AutoString(length=50), nullable=False), + sa.Column("description", sqlmodel.AutoString(length=500), nullable=True), + sa.Column("source", sqlmodel.AutoString(length=50), nullable=True), sa.Column("density_kg_m3", sa.Float(), nullable=True), sa.Column("is_crm", sa.Boolean(), nullable=True), sa.Column("id", sa.Integer(), nullable=False), @@ -47,7 +48,7 @@ def upgrade() -> None: "newslettersubscriber", sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), - sa.Column("email", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("email", sqlmodel.AutoString(), nullable=False), sa.Column("id", sa.Uuid(), nullable=False), sa.Column("is_confirmed", sa.Boolean(), nullable=False), sa.PrimaryKeyConstraint("id"), @@ -57,9 +58,9 @@ def upgrade() -> None: "organization", sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), - sa.Column("name", sqlmodel.sql.sqltypes.AutoString(length=50), nullable=False), - sa.Column("location", sqlmodel.sql.sqltypes.AutoString(length=50), nullable=True), - sa.Column("description", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True), + sa.Column("name", sqlmodel.AutoString(length=50), nullable=False), + sa.Column("location", sqlmodel.AutoString(length=50), nullable=True), + sa.Column("description", sqlmodel.AutoString(length=500), nullable=True), sa.Column("id", sa.Uuid(), nullable=False), sa.Column("owner_id", sa.Uuid(), nullable=False), sa.ForeignKeyConstraint(["owner_id"], ["user.id"], name="fk_organization_owner", use_alter=True), @@ -70,8 +71,8 @@ def upgrade() -> None: "producttype", sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), - sa.Column("name", sqlmodel.sql.sqltypes.AutoString(length=50), nullable=False), - sa.Column("description", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True), + sa.Column("name", sqlmodel.AutoString(length=50), nullable=False), + sa.Column("description", sqlmodel.AutoString(length=500), nullable=True), sa.Column("id", sa.Integer(), nullable=False), sa.PrimaryKeyConstraint("id"), ) @@ -80,8 +81,8 @@ def upgrade() -> None: "taxonomy", sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), - sa.Column("name", sqlmodel.sql.sqltypes.AutoString(length=50), nullable=False), - sa.Column("description", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True), + sa.Column("name", sqlmodel.AutoString(length=50), nullable=False), + sa.Column("description", sqlmodel.AutoString(length=500), nullable=True), sa.Column( "domains", postgresql.ARRAY( @@ -89,7 +90,7 @@ def upgrade() -> None: ), nullable=True, ), - sa.Column("source", sqlmodel.sql.sqltypes.AutoString(length=50), nullable=True), + sa.Column("source", sqlmodel.AutoString(length=50), nullable=True), sa.Column("id", sa.Integer(), nullable=False), sa.PrimaryKeyConstraint("id"), ) @@ -97,14 +98,14 @@ def upgrade() -> None: op.create_table( "user", sa.Column("id", sa.Uuid(), nullable=False), - sa.Column("email", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("hashed_password", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("email", sqlmodel.AutoString(), nullable=False), + sa.Column("hashed_password", sqlmodel.AutoString(), nullable=False), sa.Column("is_active", sa.Boolean(), nullable=False), sa.Column("is_superuser", sa.Boolean(), nullable=False), sa.Column("is_verified", sa.Boolean(), nullable=False), sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), - sa.Column("username", sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column("username", sqlmodel.AutoString(), nullable=True), sa.Column("organization_id", sa.Uuid(), nullable=True), sa.Column( "organization_role", @@ -120,12 +121,12 @@ def upgrade() -> None: "camera", sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), - sa.Column("name", sqlmodel.sql.sqltypes.AutoString(length=50), nullable=False), - sa.Column("description", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True), - sa.Column("url", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("name", sqlmodel.AutoString(length=50), nullable=False), + sa.Column("description", sqlmodel.AutoString(length=500), nullable=True), + sa.Column("url", sqlmodel.AutoString(), nullable=False), sa.Column("id", sa.Uuid(), nullable=False), - sa.Column("encrypted_api_key", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("encrypted_auth_headers", sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column("encrypted_api_key", sqlmodel.AutoString(), nullable=False), + sa.Column("encrypted_auth_headers", sqlmodel.AutoString(), nullable=True), sa.Column("owner_id", sa.Uuid(), nullable=False), sa.ForeignKeyConstraint( ["owner_id"], @@ -138,9 +139,9 @@ def upgrade() -> None: "category", sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), - sa.Column("name", sqlmodel.sql.sqltypes.AutoString(length=250), nullable=False), - sa.Column("description", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True), - sa.Column("external_id", sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column("name", sqlmodel.AutoString(length=250), nullable=False), + sa.Column("description", sqlmodel.AutoString(length=500), nullable=True), + sa.Column("external_id", sqlmodel.AutoString(), nullable=True), sa.Column("id", sa.Integer(), nullable=False), sa.Column("supercategory_id", sa.Integer(), nullable=True), sa.Column("taxonomy_id", sa.Integer(), nullable=False), @@ -161,12 +162,12 @@ def upgrade() -> None: sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("id", sa.Uuid(), nullable=False), sa.Column("user_id", sa.Uuid(), nullable=False), - sa.Column("oauth_name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("access_token", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("oauth_name", sqlmodel.AutoString(), nullable=False), + sa.Column("access_token", sqlmodel.AutoString(), nullable=False), sa.Column("expires_at", sa.Integer(), nullable=True), - sa.Column("refresh_token", sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.Column("account_id", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("account_email", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("refresh_token", sqlmodel.AutoString(), nullable=True), + sa.Column("account_id", sqlmodel.AutoString(), nullable=False), + sa.Column("account_email", sqlmodel.AutoString(), nullable=False), sa.ForeignKeyConstraint( ["user_id"], ["user.id"], @@ -179,11 +180,11 @@ def upgrade() -> None: "product", sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), - sa.Column("name", sqlmodel.sql.sqltypes.AutoString(length=50), nullable=False), - sa.Column("description", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True), - sa.Column("brand", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), - sa.Column("model", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), - sa.Column("dismantling_notes", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True), + sa.Column("name", sqlmodel.AutoString(length=50), nullable=False), + sa.Column("description", sqlmodel.AutoString(length=500), nullable=True), + sa.Column("brand", sqlmodel.AutoString(length=100), nullable=True), + sa.Column("model", sqlmodel.AutoString(length=100), nullable=True), + sa.Column("dismantling_notes", sqlmodel.AutoString(length=500), nullable=True), sa.Column("dismantling_time_start", sa.TIMESTAMP(timezone=True), nullable=False), sa.Column("dismantling_time_end", sa.TIMESTAMP(timezone=True), nullable=True), sa.Column("id", sa.Integer(), nullable=False), @@ -238,10 +239,10 @@ def upgrade() -> None: "file", sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), - sa.Column("description", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True), + sa.Column("description", sqlmodel.AutoString(length=500), nullable=True), sa.Column("id", sa.Uuid(), nullable=False), - sa.Column("filename", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("file", app.api.file_storage.models.custom_types.FileType(), nullable=False), + sa.Column("filename", sqlmodel.AutoString(), nullable=False), + sa.Column("file", file_storage_custom_types.FileType(), nullable=False), sa.Column( "parent_type", postgresql.ENUM("PRODUCT", "PRODUCT_TYPE", "MATERIAL", name="fileparenttype", create_type=False), @@ -268,11 +269,11 @@ def upgrade() -> None: "image", sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), - sa.Column("description", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True), + sa.Column("description", sqlmodel.AutoString(length=500), nullable=True), sa.Column("image_metadata", postgresql.JSONB(astext_type=sa.Text()), nullable=True), sa.Column("id", sa.Uuid(), nullable=False), - sa.Column("filename", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("file", app.api.file_storage.models.custom_types.ImageType(), nullable=False), + sa.Column("filename", sqlmodel.AutoString(), nullable=False), + sa.Column("file", file_storage_custom_types.ImageType(), nullable=False), sa.Column( "parent_type", postgresql.ENUM("PRODUCT", "PRODUCT_TYPE", "MATERIAL", name="imageparenttype", create_type=False), @@ -337,9 +338,9 @@ def upgrade() -> None: "video", sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), - sa.Column("url", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("title", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), - sa.Column("description", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True), + sa.Column("url", sqlmodel.AutoString(), nullable=False), + sa.Column("title", sqlmodel.AutoString(length=100), nullable=True), + sa.Column("description", sqlmodel.AutoString(length=500), nullable=True), sa.Column("video_metadata", postgresql.JSONB(astext_type=sa.Text()), nullable=True), sa.Column("id", sa.Integer(), nullable=False), sa.Column("product_id", sa.Integer(), nullable=False), diff --git a/backend/alembic/versions/65da9d7e309c_increase_string_fields_max_length.py b/backend/alembic/versions/65da9d7e309c_increase_string_fields_max_length.py index a5cb94a..8023e66 100644 --- a/backend/alembic/versions/65da9d7e309c_increase_string_fields_max_length.py +++ b/backend/alembic/versions/65da9d7e309c_increase_string_fields_max_length.py @@ -28,63 +28,63 @@ def upgrade() -> None: "camera", "name", existing_type=sa.VARCHAR(length=50), - type_=sqlmodel.sql.sqltypes.AutoString(length=100), + type_=sqlmodel.AutoString(length=100), existing_nullable=False, ) op.alter_column( "material", "name", existing_type=sa.VARCHAR(length=50), - type_=sqlmodel.sql.sqltypes.AutoString(length=100), + type_=sqlmodel.AutoString(length=100), existing_nullable=False, ) op.alter_column( "material", "source", existing_type=sa.VARCHAR(length=50), - type_=sqlmodel.sql.sqltypes.AutoString(length=100), + type_=sqlmodel.AutoString(length=100), existing_nullable=True, ) op.alter_column( "organization", "name", existing_type=sa.VARCHAR(length=50), - type_=sqlmodel.sql.sqltypes.AutoString(length=100), + type_=sqlmodel.AutoString(length=100), existing_nullable=False, ) op.alter_column( "organization", "location", existing_type=sa.VARCHAR(length=50), - type_=sqlmodel.sql.sqltypes.AutoString(length=100), + type_=sqlmodel.AutoString(length=100), existing_nullable=True, ) op.alter_column( "product", "name", existing_type=sa.VARCHAR(length=50), - type_=sqlmodel.sql.sqltypes.AutoString(length=100), + type_=sqlmodel.AutoString(length=100), existing_nullable=False, ) op.alter_column( "producttype", "name", existing_type=sa.VARCHAR(length=50), - type_=sqlmodel.sql.sqltypes.AutoString(length=100), + type_=sqlmodel.AutoString(length=100), existing_nullable=False, ) op.alter_column( "taxonomy", "name", existing_type=sa.VARCHAR(length=50), - type_=sqlmodel.sql.sqltypes.AutoString(length=100), + type_=sqlmodel.AutoString(length=100), existing_nullable=False, ) op.alter_column( "taxonomy", "source", existing_type=sa.VARCHAR(length=50), - type_=sqlmodel.sql.sqltypes.AutoString(length=500), + type_=sqlmodel.AutoString(length=500), existing_nullable=True, ) # ### end Alembic commands ### @@ -95,63 +95,63 @@ def downgrade() -> None: op.alter_column( "taxonomy", "source", - existing_type=sqlmodel.sql.sqltypes.AutoString(length=500), + existing_type=sqlmodel.AutoString(length=500), type_=sa.VARCHAR(length=50), existing_nullable=True, ) op.alter_column( "taxonomy", "name", - existing_type=sqlmodel.sql.sqltypes.AutoString(length=100), + existing_type=sqlmodel.AutoString(length=100), type_=sa.VARCHAR(length=50), existing_nullable=False, ) op.alter_column( "producttype", "name", - existing_type=sqlmodel.sql.sqltypes.AutoString(length=100), + existing_type=sqlmodel.AutoString(length=100), type_=sa.VARCHAR(length=50), existing_nullable=False, ) op.alter_column( "product", "name", - existing_type=sqlmodel.sql.sqltypes.AutoString(length=100), + existing_type=sqlmodel.AutoString(length=100), type_=sa.VARCHAR(length=50), existing_nullable=False, ) op.alter_column( "organization", "location", - existing_type=sqlmodel.sql.sqltypes.AutoString(length=100), + existing_type=sqlmodel.AutoString(length=100), type_=sa.VARCHAR(length=50), existing_nullable=True, ) op.alter_column( "organization", "name", - existing_type=sqlmodel.sql.sqltypes.AutoString(length=100), + existing_type=sqlmodel.AutoString(length=100), type_=sa.VARCHAR(length=50), existing_nullable=False, ) op.alter_column( "material", "source", - existing_type=sqlmodel.sql.sqltypes.AutoString(length=100), + existing_type=sqlmodel.AutoString(length=100), type_=sa.VARCHAR(length=50), existing_nullable=True, ) op.alter_column( "material", "name", - existing_type=sqlmodel.sql.sqltypes.AutoString(length=100), + existing_type=sqlmodel.AutoString(length=100), type_=sa.VARCHAR(length=50), existing_nullable=False, ) op.alter_column( "camera", "name", - existing_type=sqlmodel.sql.sqltypes.AutoString(length=100), + existing_type=sqlmodel.AutoString(length=100), type_=sa.VARCHAR(length=50), existing_nullable=False, ) diff --git a/backend/alembic/versions/95cc94317b69_add_version_to_taxonomy_model.py b/backend/alembic/versions/95cc94317b69_add_version_to_taxonomy_model.py index 56d2551..82e7282 100644 --- a/backend/alembic/versions/95cc94317b69_add_version_to_taxonomy_model.py +++ b/backend/alembic/versions/95cc94317b69_add_version_to_taxonomy_model.py @@ -24,7 +24,7 @@ def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.add_column("taxonomy", sa.Column("version", sqlmodel.sql.sqltypes.AutoString(length=50), nullable=True)) + op.add_column("taxonomy", sa.Column("version", sqlmodel.AutoString(length=50), nullable=True)) # ### end Alembic commands ### diff --git a/backend/alembic/versions/b43d157d07f1_add_basic_circularity_properties_model.py b/backend/alembic/versions/b43d157d07f1_add_basic_circularity_properties_model.py index 0e5a745..ae6025d 100644 --- a/backend/alembic/versions/b43d157d07f1_add_basic_circularity_properties_model.py +++ b/backend/alembic/versions/b43d157d07f1_add_basic_circularity_properties_model.py @@ -28,15 +28,15 @@ def upgrade() -> None: "circularityproperties", sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()"), nullable=True), - sa.Column("recyclability_observation", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=False), - sa.Column("recyclability_comment", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), - sa.Column("recyclability_reference", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), - sa.Column("repairability_observation", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=False), - sa.Column("repairability_comment", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), - sa.Column("repairability_reference", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), - sa.Column("remanufacturability_observation", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=False), - sa.Column("remanufacturability_comment", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), - sa.Column("remanufacturability_reference", sqlmodel.sql.sqltypes.AutoString(length=100), nullable=True), + sa.Column("recyclability_observation", sqlmodel.AutoString(length=500), nullable=False), + sa.Column("recyclability_comment", sqlmodel.AutoString(length=100), nullable=True), + sa.Column("recyclability_reference", sqlmodel.AutoString(length=100), nullable=True), + sa.Column("repairability_observation", sqlmodel.AutoString(length=500), nullable=False), + sa.Column("repairability_comment", sqlmodel.AutoString(length=100), nullable=True), + sa.Column("repairability_reference", sqlmodel.AutoString(length=100), nullable=True), + sa.Column("remanufacturability_observation", sqlmodel.AutoString(length=500), nullable=False), + sa.Column("remanufacturability_comment", sqlmodel.AutoString(length=100), nullable=True), + sa.Column("remanufacturability_reference", sqlmodel.AutoString(length=100), nullable=True), sa.Column("id", sa.Integer(), nullable=False), sa.Column("product_id", sa.Integer(), nullable=False), sa.ForeignKeyConstraint( From dc14b960094fc4558e3e8ff7c65ce5dbacfefed1 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 17 Feb 2026 15:42:13 +0100 Subject: [PATCH 77/79] feature(backend): WIP: test suite --- backend/tests/api/__init__.py | 1 + .../api/test_background_data_endpoints.py | 213 +++++++ backend/tests/conftest.py | 192 ++++--- backend/tests/factories/__init__.py | 5 +- backend/tests/factories/emails.py | 95 ++- backend/tests/factories/models.py | 214 +++++++ backend/tests/fixtures/__init__.py | 0 backend/tests/fixtures/client.py | 64 +++ backend/tests/fixtures/data.py | 94 +++ backend/tests/fixtures/database.py | 46 ++ backend/tests/fixtures/migrations.py | 149 +++++ backend/tests/integration/__init__.py | 1 + backend/tests/integration/test_auth_models.py | 488 ++++++++++++++++ .../test_background_data_models.py | 289 ++++++++++ .../integration/test_database_operations.py | 334 +++++++++++ backend/tests/test_main.py | 22 - backend/tests/test_migrations.py | 29 + backend/tests/unit/__init__.py | 1 + backend/tests/unit/auth/test_auth_utils.py | 208 +++++++ .../test_background_data_crud.py | 131 +++++ .../test_data_collection_crud.py | 121 ++++ .../tests/{tests => unit}/emails/__init__.py | 0 .../emails/test_programmatic_emails.py | 18 +- backend/tests/unit/test_auth_exceptions.py | 465 +++++++++++++++ .../unit/test_background_data_schemas.py | 163 ++++++ backend/tests/unit/test_common_utils.py | 252 ++++++++ backend/tests/unit/test_core_config.py | 283 +++++++++ .../unit/test_data_collection_schemas.py | 539 ++++++++++++++++++ .../tests/unit/test_ownership_validation.py | 530 +++++++++++++++++ .../tests/unit/test_validation_patterns.py | 385 +++++++++++++ 30 files changed, 5204 insertions(+), 128 deletions(-) create mode 100644 backend/tests/api/__init__.py create mode 100755 backend/tests/api/test_background_data_endpoints.py create mode 100644 backend/tests/factories/models.py create mode 100644 backend/tests/fixtures/__init__.py create mode 100644 backend/tests/fixtures/client.py create mode 100644 backend/tests/fixtures/data.py create mode 100644 backend/tests/fixtures/database.py create mode 100644 backend/tests/fixtures/migrations.py create mode 100644 backend/tests/integration/__init__.py create mode 100644 backend/tests/integration/test_auth_models.py create mode 100644 backend/tests/integration/test_background_data_models.py create mode 100644 backend/tests/integration/test_database_operations.py delete mode 100644 backend/tests/test_main.py create mode 100644 backend/tests/test_migrations.py create mode 100644 backend/tests/unit/__init__.py create mode 100644 backend/tests/unit/auth/test_auth_utils.py create mode 100644 backend/tests/unit/background_data/test_background_data_crud.py create mode 100644 backend/tests/unit/data_collection/test_data_collection_crud.py rename backend/tests/{tests => unit}/emails/__init__.py (100%) rename backend/tests/{tests => unit}/emails/test_programmatic_emails.py (94%) create mode 100644 backend/tests/unit/test_auth_exceptions.py create mode 100644 backend/tests/unit/test_background_data_schemas.py create mode 100644 backend/tests/unit/test_common_utils.py create mode 100644 backend/tests/unit/test_core_config.py create mode 100644 backend/tests/unit/test_data_collection_schemas.py create mode 100644 backend/tests/unit/test_ownership_validation.py create mode 100644 backend/tests/unit/test_validation_patterns.py diff --git a/backend/tests/api/__init__.py b/backend/tests/api/__init__.py new file mode 100644 index 0000000..1c4bb23 --- /dev/null +++ b/backend/tests/api/__init__.py @@ -0,0 +1 @@ +"""API/E2E tests package.""" diff --git a/backend/tests/api/test_background_data_endpoints.py b/backend/tests/api/test_background_data_endpoints.py new file mode 100755 index 0000000..5a3e978 --- /dev/null +++ b/backend/tests/api/test_background_data_endpoints.py @@ -0,0 +1,213 @@ +"""API endpoint tests for background data (E2E tests).""" + +import pytest +from dirty_equals import IsInt, IsList, IsPositive, IsStr +from httpx import AsyncClient +from sqlalchemy.ext.asyncio import AsyncSession + +from app.api.background_data.models import Category, Taxonomy, TaxonomyDomain + + +@pytest.mark.api +class TestTaxonomyAPI: + """Test Taxonomy API endpoints.""" + + async def test_create_taxonomy(self, superuser_client: AsyncClient): + """Test POST /admin/taxonomies creates a taxonomy.""" + data = { + "name": "Test API Taxonomy", + "version": "v1.0.0", + "description": "Created via API", + "domains": ["materials"], + } + + response = await superuser_client.post("/admin/taxonomies", json=data) + + if response.status_code != 201: + print(f"\nResponse status: {response.status_code}") + print(f"Response Content: {response.text}") + assert response.status_code == 201 + json_data = response.json() + assert json_data["name"] == "Test API Taxonomy" + assert json_data["version"] == "v1.0.0" + assert "id" in json_data + assert "created_at" in json_data + + async def test_get_taxonomy(self, async_client: AsyncClient, db_taxonomy: Taxonomy): + """Test GET /taxonomies/{id} retrieves a taxonomy.""" + response = await async_client.get(f"/taxonomies/{db_taxonomy.id}") + + assert response.status_code == 200 + json_data = response.json() + assert json_data["id"] == db_taxonomy.id + assert json_data["name"] == db_taxonomy.name + + async def test_get_nonexistent_taxonomy(self, async_client: AsyncClient): + """Test GET /taxonomies/{id} with non-existent ID returns 404.""" + response = await async_client.get("/taxonomies/99999") + assert response.status_code == 404 + + async def test_list_taxonomies(self, async_client: AsyncClient, session: AsyncSession): + """Test GET /taxonomies returns list of taxonomies.""" + # Create a few taxonomies + for i in range(3): + taxonomy = Taxonomy( + name=f"Taxonomy {i}", + version=f"v{i}.0.0", + domains={TaxonomyDomain.MATERIALS}, + ) + session.add(taxonomy) + await session.flush() + + response = await async_client.get("/taxonomies") + + assert response.status_code == 200 + json_data = response.json() + assert isinstance(json_data, list) + assert len(json_data) >= 3 + + async def test_update_taxonomy(self, superuser_client: AsyncClient, db_taxonomy: Taxonomy): + """Test PATCH /admin/taxonomies/{id} updates a taxonomy.""" + update_data = { + "name": "Updated Taxonomy Name", + "version": "v2.0.0", + } + + response = await superuser_client.patch(f"/admin/taxonomies/{db_taxonomy.id}", json=update_data) + + if response.status_code != 200: + print(f"DEBUG: {response.json()}") + assert response.status_code == 200 + json_data = response.json() + assert json_data["name"] == "Updated Taxonomy Name" + assert json_data["version"] == "v2.0.0" + + async def test_delete_taxonomy(self, superuser_client: AsyncClient, db_taxonomy: Taxonomy): + """Test DELETE /admin/taxonomies/{id} deletes a taxonomy.""" + response = await superuser_client.delete(f"/admin/taxonomies/{db_taxonomy.id}") + + assert response.status_code == 204 + + # Verify it's deleted + get_response = await superuser_client.get(f"/taxonomies/{db_taxonomy.id}") + assert get_response.status_code == 404 + + +@pytest.mark.api +class TestCategoryAPI: + """Test Category API endpoints.""" + + async def test_create_category(self, superuser_client: AsyncClient, db_taxonomy: Taxonomy): + """Test POST /admin/categories creates a category.""" + data = { + "name": "Test API Category", + "description": "Created via API", + "taxonomy_id": db_taxonomy.id, + } + + response = await superuser_client.post("/admin/categories", json=data) + + assert response.status_code == 201 + json_data = response.json() + assert json_data["name"] == "Test API Category" + assert json_data["taxonomy_id"] == db_taxonomy.id + + async def test_get_category(self, async_client: AsyncClient, db_category: Category): + """Test GET /categories/{id} retrieves a category.""" + response = await async_client.get(f"/categories/{db_category.id}") + + assert response.status_code == 200 + json_data = response.json() + assert json_data["id"] == db_category.id + assert json_data["name"] == db_category.name + + async def test_create_category_with_subcategories(self, superuser_client: AsyncClient, db_taxonomy: Taxonomy): + """Test creating category with nested subcategories.""" + data = { + "name": "Parent Category", + "taxonomy_id": db_taxonomy.id, + "subcategories": [{"name": "Child Category", "subcategories": [{"name": "Grandchild Category"}]}], + } + + response = await superuser_client.post("/admin/categories", json=data) + + if response.status_code != 201: + print(f"DEBUG: {response.json()}") + assert response.status_code == 201 + json_data = response.json() + assert json_data["name"] == "Parent Category" + # Verify subcategories were created (depending on endpoint response structure) + + +@pytest.mark.api +class TestMaterialAPI: + """Test Material API endpoints.""" + + async def test_create_material(self, superuser_client: AsyncClient): + """Test POST /admin/materials creates a material.""" + data = { + "name": "Test API Material", + "description": "Created via API", + "density_kg_m3": 8000.0, + "is_crm": True, + } + + response = await superuser_client.post("/admin/materials", json=data) + + assert response.status_code == 201 + json_data = response.json() + assert json_data["name"] == "Test API Material" + assert json_data["density_kg_m3"] == 8000.0 + + async def test_create_material_with_invalid_density(self, superuser_client: AsyncClient): + """Test POST /admin/materials with negative density fails.""" + data = { + "name": "Invalid Material", + "density_kg_m3": -100.0, + } + + response = await superuser_client.post("/admin/materials", json=data) + assert response.status_code == 422 # Validation error + + +@pytest.mark.api +class TestProductTypeAPI: + """Test ProductType API endpoints.""" + + async def test_create_product_type(self, superuser_client: AsyncClient): + """Test POST /admin/product-types creates a product type.""" + data = { + "name": "Test API Product Type", + "description": "Created via API", + } + + response = await superuser_client.post("/admin/product-types", json=data) + + assert response.status_code == 201 + json_data = response.json() + assert json_data["name"] == "Test API Product Type" + + +@pytest.mark.api +@pytest.mark.slow +class TestAPIWithDirtyEquals: + """Example tests using dirty-equals for flexible assertions.""" + + async def test_taxonomy_response_structure(self, async_client: AsyncClient, db_taxonomy: Taxonomy): + """Test taxonomy response has expected structure using dirty-equals.""" + response = await async_client.get(f"/taxonomies/{db_taxonomy.id}") + + assert response.status_code == 200 + json_data = response.json() + + # Use dirty-equals for flexible type checking + assert json_data == { + "id": IsInt & IsPositive, + "name": IsStr, + "version": IsStr | None, + "description": IsStr | None, + "domains": ["materials"], + "source": IsStr | None, + "created_at": IsStr, + "updated_at": IsStr, + } diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 889d359..31e64fe 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -1,145 +1,163 @@ -"""Test configuration file. +"""Root test configuration with modern 2026 best practices. -Inspired by https://medium.com/@gnetkov/testing-fastapi-application-with-pytest-57080960fd62. +This conftest provides: +- Database setup with transaction isolation +- Async HTTP client using httpx (via plugins) +- Factory fixtures (via plugins) +- Common test utilities (via plugins) +- Mocking utilities via pytest-mock (mocker fixture auto-injected) + +Key Fixtures: +- session: Isolated async database session with transaction rollback """ import logging from collections.abc import AsyncGenerator, Generator from pathlib import Path -from unittest.mock import AsyncMock import pytest from alembic.config import Config -from fastapi.testclient import TestClient from sqlalchemy import Engine, create_engine, text from sqlalchemy.exc import ProgrammingError -from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine -from sqlalchemy.ext.asyncio.engine import AsyncEngine +from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker, create_async_engine from sqlmodel.ext.asyncio.session import AsyncSession from alembic import command from app.core.config import settings -from app.main import app - -from .factories.emails import EmailContextFactory, EmailDataFactory # Set up logger -logger: logging.Logger = logging.getLogger(__name__) +logger = logging.getLogger(__name__) + +# Register plugins for fixture discovery +pytest_plugins = [ + "tests.fixtures.client", + "tests.fixtures.data", + "tests.fixtures.database", +] -# Set up sync engine for test database construction +# ============================================================================ +# Database Setup +# ============================================================================ + +# Sync engine for database creation/destruction sync_engine: Engine = create_engine(settings.sync_database_url, isolation_level="AUTOCOMMIT") -# Set up an async test engine for the actual -TEST_SQLALCHEMY_DATABASE_URL: str = settings.async_test_database_url +# Async engine for tests +# Async engine for tests +TEST_DATABASE_URL: str = settings.async_test_database_url TEST_DATABASE_NAME: str = settings.postgres_test_db -async_engine: AsyncEngine = create_async_engine(TEST_SQLALCHEMY_DATABASE_URL, echo=settings.debug) +# Use NullPool to ensure connections are closed after each test and not reused across loops +from sqlalchemy.pool import NullPool + +async_engine: AsyncEngine = create_async_engine(TEST_DATABASE_URL, echo=False, future=True, poolclass=NullPool) async_session_local = async_sessionmaker( - bind=async_engine, autocommit=False, autoflush=False, class_=AsyncSession, expire_on_commit=False + bind=async_engine, + class_=AsyncSession, + autocommit=False, + autoflush=False, + expire_on_commit=False, ) -### Set up bare test database using sync engine def create_test_database() -> None: - """Create the test database if it doesn't exist.""" + """Create the test database. Recreate if it exists.""" with sync_engine.connect() as connection: - try: - connection.execute(text(f"CREATE DATABASE {TEST_DATABASE_NAME}")) - logger.info("Test database created successfully.") - except ProgrammingError: - logger.info("Test database already exists, continuing...") + # Terminate connections to allow drop + connection.execute( + text( + f""" + SELECT pg_terminate_backend(pg_stat_activity.pid) + FROM pg_stat_activity + WHERE pg_stat_activity.datname = '{TEST_DATABASE_NAME}' + AND pid <> pg_backend_pid(); + """ + ) + ) + connection.execute(text(f"DROP DATABASE IF EXISTS {TEST_DATABASE_NAME}")) + connection.execute(text(f"CREATE DATABASE {TEST_DATABASE_NAME}")) + logger.info("Test database created successfully.") def get_alembic_config() -> Config: - """Get Alembic config for tests.""" + """Get Alembic config for running migrations in tests.""" alembic_cfg = Config() project_root: Path = Path(__file__).parents[1] alembic_cfg.set_main_option("script_location", str(project_root / "alembic")) - alembic_cfg.set_main_option("sqlalchemy.url", TEST_SQLALCHEMY_DATABASE_URL) + alembic_cfg.set_main_option("script_location", str(project_root / "alembic")) + alembic_cfg.set_main_option("sqlalchemy.url", settings.sync_test_database_url) + alembic_cfg.set_main_option("is_test", "true") return alembic_cfg @pytest.fixture(scope="session") -def setup_test_database() -> Generator: - """Create test database, run migrations, and cleanup after tests.""" - create_test_database() # Create empty database +def setup_test_database() -> Generator[None]: + """Create test database and run migrations once per test session.""" + create_test_database() - # Run migrations + # Run migrations to latest alembic_cfg: Config = get_alembic_config() + print("Running Alembic upgrade head...") command.upgrade(alembic_cfg, "head") + print("Alembic upgrade complete.") yield - # Cleanup - with sync_engine.connect() as connection: - connection.execute(text("DROP DATABASE IF EXISTS " + TEST_DATABASE_NAME)) + # Dispose async engine connections before dropping database + import asyncio + asyncio.run(async_engine.dispose()) -### Async test session generators -@pytest.fixture -async def get_async_session() -> AsyncGenerator[AsyncSession]: - """Create a new database session for each test and roll it back after the test.""" - async with async_engine.begin() as connection, async_session_local(bind=connection) as session: - transaction = await connection.begin_nested() - yield session - await transaction.rollback() + # Cleanup + with sync_engine.connect() as connection: + # Terminate other connections to the database to ensure DROP works + connection.execute( + text( + f""" + SELECT pg_terminate_backend(pg_stat_activity.pid) + FROM pg_stat_activity + WHERE pg_stat_activity.datname = '{TEST_DATABASE_NAME}' + AND pid <> pg_backend_pid(); + """ + ) + ) + connection.execute(text(f"DROP DATABASE IF EXISTS {TEST_DATABASE_NAME}")) @pytest.fixture -async def client(db: AsyncSession) -> AsyncGenerator[TestClient]: - """Provide a TestClient that uses the test database session.""" - - async def override_get_db() -> AsyncGenerator[AsyncSession]: - yield db +async def session(setup_test_database: None) -> AsyncGenerator[AsyncSession]: + """Provide isolated database session using transaction rollback. - app.dependency_overrides[get_async_session] = override_get_db - - with TestClient(app) as c: - yield c + This uses the 'connection.begin()' pattern which is more robust for async tests + than the nested transaction approach. + """ + async with async_engine.connect() as connection: + # Begin a transaction that will be rolled back + transaction = await connection.begin() - app.dependency_overrides.clear() + # Bind the session to this specific connection + session_factory = async_sessionmaker( + bind=connection, + class_=AsyncSession, + autocommit=False, + autoflush=False, + expire_on_commit=False, + ) + async with session_factory() as session: + yield session -### Email fixtures -@pytest.fixture -def email_context() -> EmailContextFactory: - """Return a realistic email template context dict using FactoryBoy/Faker.""" - return EmailContextFactory() + # Rollback the transaction after the test completes + if transaction.is_active: + await transaction.rollback() -@pytest.fixture -def email_data() -> EmailDataFactory: - """Return realistic test data for email functions using FactoryBoy/Faker.""" - return EmailDataFactory() +# ============================================================================ +# Utility Fixtures +# ============================================================================ @pytest.fixture -def mock_smtp() -> AsyncMock: - """Return a configured mock SMTP client for testing email sending.""" - mock = AsyncMock() - mock.connect = AsyncMock() - mock.login = AsyncMock() - mock.send_message = AsyncMock() - mock.quit = AsyncMock() - return mock - - -@pytest.fixture -def mock_email_sender(monkeypatch: pytest.MonkeyPatch) -> AsyncMock: - """Mock the fastapi-mail send_message function for all email tests. - - This fixture automatically patches fm.send_message so tests don't need - to manually patch it with context managers. - - Returns: - AsyncMock: The mocked send_message function - - Usage: - @pytest.mark.asyncio - async def test_send_email(mock_email_sender): - await send_registration_email("test@example.com", "user", "token") - mock_email_sender.assert_called_once() - """ - mock_send = AsyncMock() - monkeypatch.setattr("app.api.auth.utils.programmatic_emails.fm.send_message", mock_send) - return mock_send +def anyio_backend(): + """Configure anyio backend for async tests.""" + return "asyncio" diff --git a/backend/tests/factories/__init__.py b/backend/tests/factories/__init__.py index df3d47b..8fa6b52 100644 --- a/backend/tests/factories/__init__.py +++ b/backend/tests/factories/__init__.py @@ -1 +1,4 @@ -"""Factory-boy factories for generating test objects.""" +"""Factories package. + +Contains Polyfactory model factories and TypedDict factories. +""" diff --git a/backend/tests/factories/emails.py b/backend/tests/factories/emails.py index 1974be0..5b24501 100644 --- a/backend/tests/factories/emails.py +++ b/backend/tests/factories/emails.py @@ -1,26 +1,87 @@ -"""Factories for email template context dicts for tests.""" +"""Factories for email template context dicts for tests. -from factory.base import DictFactory -from factory.faker import Faker +Using Polyfactory TypedDictFactory to replace legacy FactoryBoy DictFactory. +""" +from typing import TypedDict +from polyfactory.factories.typed_dict_factory import TypedDictFactory -class EmailContextFactory(DictFactory): + +class EmailContext(TypedDict): + """Type definition for email context.""" + username: str + verification_link: str + reset_link: str + confirmation_link: str + unsubscribe_link: str + subject: str + newsletter_content: str + + +class EmailContextFactory(TypedDictFactory[EmailContext]): """Produce realistic email template context dicts for tests.""" + + __model__ = EmailContext + + @classmethod + def username(cls) -> str: + return cls.__faker__.user_name() + + @classmethod + def verification_link(cls) -> str: + return cls.__faker__.url() + + @classmethod + def reset_link(cls) -> str: + return cls.__faker__.url() + + @classmethod + def confirmation_link(cls) -> str: + return cls.__faker__.url() + + @classmethod + def unsubscribe_link(cls) -> str: + return cls.__faker__.url() - username = Faker("user_name") - verification_link = Faker("url") - reset_link = Faker("url") - confirmation_link = Faker("url") - unsubscribe_link = Faker("url") - subject = Faker("sentence", nb_words=5) - newsletter_content = Faker("text", max_nb_chars=200) + @classmethod + def subject(cls) -> str: + return cls.__faker__.sentence(nb_words=5) + @classmethod + def newsletter_content(cls) -> str: + return cls.__faker__.text(max_nb_chars=200) -class EmailDataFactory(DictFactory): + +class EmailData(TypedDict): + """Type definition for email data.""" + email: str + username: str + token: str + subject: str + body: str + + +class EmailDataFactory(TypedDictFactory[EmailData]): """Produce test data for email sending functions.""" + + __model__ = EmailData + + @classmethod + def email(cls) -> str: + return cls.__faker__.email() + + @classmethod + def username(cls) -> str: + return cls.__faker__.user_name() + + @classmethod + def token(cls) -> str: + return str(cls.__faker__.uuid4()) + + @classmethod + def subject(cls) -> str: + return cls.__faker__.sentence(nb_words=5) - email = Faker("email") - username = Faker("user_name") - token = Faker("uuid4") - subject = Faker("sentence", nb_words=5) - body = Faker("text", max_nb_chars=200) + @classmethod + def body(cls) -> str: + return cls.__faker__.text(max_nb_chars=200) diff --git a/backend/tests/factories/models.py b/backend/tests/factories/models.py new file mode 100644 index 0000000..892e9cf --- /dev/null +++ b/backend/tests/factories/models.py @@ -0,0 +1,214 @@ +"""Modern test factories using polyfactory for background data models. + +Polyfactory provides better Pydantic v2 support and native async capabilities. +""" + +from typing import Generic, TypeVar + +from polyfactory.factories.sqlalchemy_factory import SQLAlchemyFactory +from sqlalchemy.ext.asyncio import AsyncSession + +from app.api.auth.models import User +from app.api.background_data.models import ( + Category, + CategoryMaterialLink, + CategoryProductTypeLink, + Material, + ProductType, + Taxonomy, + TaxonomyDomain, +) + +T = TypeVar("T") + + +class BaseModelFactory(Generic[T], SQLAlchemyFactory[T]): + """Base factory with custom create_async support for explicit session.""" + + __is_base_factory__ = True + __set_relationships__ = False # Skip relationship introspection to avoid SQLAlchemy/polyfactory conflicts + + @classmethod + async def create_async(cls, session: AsyncSession | None = None, **kwargs) -> T: + """Create a new instance, optionally using a provided session.""" + if session: + instance = cls.build(**kwargs) + session.add(instance) + await session.commit() + await session.refresh(instance) + return instance + return await super().create_async(**kwargs) + + +class UserFactory(BaseModelFactory[User]): + """Factory for creating User test instances.""" + + __model__ = User + + @classmethod + def email(cls) -> str: + return cls.__faker__.email() + + @classmethod + def hashed_password(cls) -> str: + return "not_really_hashed" + + @classmethod + def is_active(cls) -> bool: + return True + + @classmethod + def is_superuser(cls) -> bool: + return False + + @classmethod + def is_verified(cls) -> bool: + return True + + @classmethod + def username(cls) -> str: + return cls.__faker__.user_name() + + @classmethod + def organization(cls) -> None: + return None + + @classmethod + def organization_id(cls) -> None: + return None + + @classmethod + def owned_organization(cls) -> None: + return None + + @classmethod + def products(cls) -> list: + return [] + + @classmethod + def oauth_accounts(cls) -> list: + return [] + + +class TaxonomyFactory(BaseModelFactory[Taxonomy]): + """Factory for creating Taxonomy test instances.""" + + __model__ = Taxonomy + + @classmethod + def name(cls) -> str: + return cls.__faker__.catch_phrase() + + @classmethod + def version(cls) -> str: + return cls.__faker__.numerify(text="v#.#.#") + + @classmethod + def description(cls) -> str | None: + return cls.__faker__.text(max_nb_chars=200) if cls.__faker__.boolean() else None + + @classmethod + def domains(cls) -> set[TaxonomyDomain]: + # Return at least one domain + domains = [TaxonomyDomain.MATERIALS] + if cls.__faker__.boolean(): + domains.append(TaxonomyDomain.PRODUCTS) + return set(domains) + + @classmethod + def categories(cls) -> list[Category]: + return [] + + @classmethod + def source(cls) -> str | None: + return cls.__faker__.url() if cls.__faker__.boolean() else None + + +class CategoryFactory(BaseModelFactory[Category]): + """Factory for creating Category test instances.""" + + __model__ = Category + + @classmethod + def name(cls) -> str: + return cls.__faker__.word().title() + + @classmethod + def description(cls) -> str | None: + return cls.__faker__.sentence() if cls.__faker__.boolean() else None + + @classmethod + def external_id(cls) -> str | None: + return cls.__faker__.uuid4() if cls.__faker__.boolean() else None + + @classmethod + def supercategory_id(cls) -> int | None: + return None + + @classmethod + def supercategory(cls) -> None: + return None + + # taxonomy_id and supercategory_id should be set explicitly in tests + + +class MaterialFactory(BaseModelFactory[Material]): + """Factory for creating Material test instances.""" + + __model__ = Material + + @classmethod + def name(cls) -> str: + materials = ["Steel", "Aluminum", "Copper", "Titanium", "Carbon Fiber", "Glass", "Ceramic"] + return cls.__faker__.random_element(elements=materials) + + @classmethod + def description(cls) -> str | None: + return cls.__faker__.sentence() if cls.__faker__.boolean() else None + + @classmethod + def source(cls) -> str | None: + return cls.__faker__.url() if cls.__faker__.boolean() else None + + @classmethod + def density_kg_m3(cls) -> float | None: + return ( + round(cls.__faker__.pyfloat(min_value=100, max_value=20000), 2) + if cls.__faker__.boolean(chance_of_getting_true=80) + else None + ) + + @classmethod + def is_crm(cls) -> bool | None: + return cls.__faker__.boolean() if cls.__faker__.boolean(chance_of_getting_true=80) else None + + +class ProductTypeFactory(BaseModelFactory[ProductType]): + """Factory for creating ProductType test instances.""" + + __model__ = ProductType + + @classmethod + def name(cls) -> str: + product_types = ["Electronics", "Furniture", "Appliances", "Tools", "Packaging", "Automotive Parts"] + return cls.__faker__.random_element(elements=product_types) + + @classmethod + def description(cls) -> str | None: + return cls.__faker__.sentence() if cls.__faker__.boolean() else None + + +class CategoryMaterialLinkFactory(BaseModelFactory[CategoryMaterialLink]): + """Factory for creating CategoryMaterialLink instances.""" + + __model__ = CategoryMaterialLink + + # category_id and material_id should be set explicitly + + +class CategoryProductTypeLinkFactory(BaseModelFactory[CategoryProductTypeLink]): + """Factory for creating CategoryProductTypeLink instances.""" + + __model__ = CategoryProductTypeLink + + # category_id and product_type_id should be set explicitly diff --git a/backend/tests/fixtures/__init__.py b/backend/tests/fixtures/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/tests/fixtures/client.py b/backend/tests/fixtures/client.py new file mode 100644 index 0000000..df2d836 --- /dev/null +++ b/backend/tests/fixtures/client.py @@ -0,0 +1,64 @@ +"""HTTP Client fixtures for API testing.""" + +import httpx +import pytest +from collections.abc import AsyncGenerator +from httpx import ASGITransport +from sqlalchemy.ext.asyncio import AsyncSession + +from app.core.database import get_async_session +from app.main import app + + +@pytest.fixture +def test_app(): + """Provide fresh FastAPI app instance. + + Yields app with cleared dependency overrides after each test. + """ + yield app + app.dependency_overrides.clear() + + +@pytest.fixture +async def async_client(test_app, session: AsyncSession) -> AsyncGenerator[httpx.AsyncClient]: + """Provide async HTTP client for API testing. + + Uses httpx.AsyncClient for true async testing of ASGI application. + Automatically injects test database session. + """ + + async def override_get_session() -> AsyncGenerator[AsyncSession]: + yield session + + test_app.dependency_overrides[get_async_session] = override_get_session + + async with httpx.AsyncClient( + transport=ASGITransport(app=test_app), + base_url="http://test", + follow_redirects=True, + ) as client: + yield client + + test_app.dependency_overrides.clear() + + +@pytest.fixture +async def superuser(session: AsyncSession) -> "User": + """Create a superuser for testing.""" + from app.api.auth.models import User + from tests.factories.models import UserFactory + + user = await UserFactory.create_async(session=session, is_superuser=True, is_active=True) + return user + + +@pytest.fixture +async def superuser_client(async_client: httpx.AsyncClient, superuser: "User", test_app) -> AsyncGenerator[httpx.AsyncClient, None]: + """Provide an authenticated client with superuser privileges (via dependency override).""" + from app.api.auth.dependencies import current_active_superuser + + test_app.dependency_overrides[current_active_superuser] = lambda: superuser + yield async_client + # Cleanup override + test_app.dependency_overrides.pop(current_active_superuser, None) diff --git a/backend/tests/fixtures/data.py b/backend/tests/fixtures/data.py new file mode 100644 index 0000000..e2360b8 --- /dev/null +++ b/backend/tests/fixtures/data.py @@ -0,0 +1,94 @@ +"""Data fixtures for pre-populating test database.""" + +import pytest +from sqlalchemy.ext.asyncio import AsyncSession +from app.api.background_data.models import Category, Material, ProductType, Taxonomy, TaxonomyDomain +from tests.factories.models import ( + CategoryFactory, + MaterialFactory, + ProductTypeFactory, + TaxonomyFactory, +) + + +@pytest.fixture +async def db_taxonomy(session: AsyncSession) -> Taxonomy: + """Create and return a test taxonomy in database.""" + taxonomy = Taxonomy( + name="Test Materials Taxonomy", + version="v1.0.0", + description="A test taxonomy for materials", + domains={TaxonomyDomain.MATERIALS}, + source="https://test.example.com", + ) + session.add(taxonomy) + await session.flush() + await session.refresh(taxonomy) + return taxonomy + + +@pytest.fixture +async def db_category(session: AsyncSession, db_taxonomy: Taxonomy) -> Category: + """Create and return a test category in database.""" + category = Category( + name="Test Category", + description="A test category", + taxonomy_id=db_taxonomy.id, + ) + session.add(category) + await session.flush() + await session.refresh(category) + return category + + +@pytest.fixture +async def db_material(session: AsyncSession) -> Material: + """Create and return a test material in database.""" + material = Material( + name="Test Material", + description="A test material", + density_kg_m3=7850.0, + is_crm=True, + ) + session.add(material) + await session.flush() + await session.refresh(material) + return material + + +@pytest.fixture +async def db_product_type(session: AsyncSession) -> ProductType: + """Create and return a test product type in database.""" + product_type = ProductType( + name="Test Product Type", + description="A test product type", + ) + session.add(product_type) + await session.flush() + await session.refresh(product_type) + return product_type + + +# Factory fixtures for convenient access +@pytest.fixture +def taxonomy_factory() -> type[TaxonomyFactory]: + """Provide TaxonomyFactory.""" + return TaxonomyFactory + + +@pytest.fixture +def category_factory() -> type[CategoryFactory]: + """Provide CategoryFactory.""" + return CategoryFactory + + +@pytest.fixture +def material_factory() -> type[MaterialFactory]: + """Provide MaterialFactory.""" + return MaterialFactory + + +@pytest.fixture +def product_type_factory() -> type[ProductTypeFactory]: + """Provide ProductTypeFactory.""" + return ProductTypeFactory diff --git a/backend/tests/fixtures/database.py b/backend/tests/fixtures/database.py new file mode 100644 index 0000000..8b91e0b --- /dev/null +++ b/backend/tests/fixtures/database.py @@ -0,0 +1,46 @@ +"""Database fixtures and helpers for testing.""" + +import pytest +from sqlalchemy.ext.asyncio import AsyncSession +from sqlmodel import select + + +class DBOperations: + """Helper class for common database operations in tests.""" + + def __init__(self, session: AsyncSession): + self.session = session + + async def get_by_id(self, model, obj_id: int): + """Get model instance by ID.""" + return await self.session.get(model, obj_id) + + async def get_by_filter(self, model, **filters): + """Get single model instance by filters.""" + stmt = select(model).filter_by(**filters) + result = await self.session.execute(stmt) + return result.scalar_one_or_none() + + async def get_all(self, model, **filters): + """Get all model instances matching filters.""" + stmt = select(model).filter_by(**filters) + result = await self.session.execute(stmt) + return result.scalars().all() + + async def create(self, instance): + """Create instance and return it with ID.""" + self.session.add(instance) + await self.session.flush() + await self.session.refresh(instance) + return instance + + async def delete(self, instance): + """Delete instance.""" + await self.session.delete(instance) + await self.session.flush() + + +@pytest.fixture +def db_ops(session: AsyncSession) -> DBOperations: + """Provide database operations helper.""" + return DBOperations(session) diff --git a/backend/tests/fixtures/migrations.py b/backend/tests/fixtures/migrations.py new file mode 100644 index 0000000..219de90 --- /dev/null +++ b/backend/tests/fixtures/migrations.py @@ -0,0 +1,149 @@ +"""Database migration testing fixtures. + +Utilities for testing Alembic migrations, schema changes, and database evolution. +""" + +from pathlib import Path + +import pytest +from alembic import command +from alembic.config import Config +from app.core.config import settings +from sqlalchemy import Engine, create_engine, inspect, text + + +class MigrationHelper: + """Helper class for testing database migrations.""" + + def __init__(self, alembic_cfg: Config): + """Initialize migration helper with Alembic config.""" + self.alembic_cfg = alembic_cfg + self.sync_engine: Engine = create_engine( + settings.sync_database_url, + isolation_level="AUTOCOMMIT", + ) + + def upgrade(self, revision: str = "head") -> None: + """Upgrade database to specific revision. + + Args: + revision: Target revision (default: 'head' - latest) + """ + command.upgrade(self.alembic_cfg, revision) + + def downgrade(self, revision: str) -> None: + """Downgrade database to specific revision. + + Args: + revision: Target revision to downgrade to + """ + command.downgrade(self.alembic_cfg, revision) + + def current_revision(self) -> str: + """Get current database revision.""" + with self.sync_engine.connect() as connection: + result = connection.execute( + text("SELECT version_num FROM alembic_version ORDER BY version_num DESC LIMIT 1") + ) + row = result.first() + return row[0] if row else None + + def table_exists(self, table_name: str) -> bool: + """Check if table exists in database. + + Args: + table_name: Name of the table to check + + Returns: + True if table exists, False otherwise + """ + with self.sync_engine.connect() as connection: + inspector = inspect(connection) + return table_name in inspector.get_table_names() + + def column_exists(self, table_name: str, column_name: str) -> bool: + """Check if column exists in table. + + Args: + table_name: Table to check + column_name: Column to look for + + Returns: + True if column exists, False otherwise + """ + with self.sync_engine.connect() as connection: + inspector = inspect(connection) + if not self.table_exists(table_name): + return False + columns = [col["name"] for col in inspector.get_columns(table_name)] + return column_name in columns + + def get_table_columns(self, table_name: str) -> list[str]: + """Get list of column names for a table. + + Args: + table_name: Table to inspect + + Returns: + List of column names + """ + with self.sync_engine.connect() as connection: + inspector = inspect(connection) + if not self.table_exists(table_name): + return [] + return [col["name"] for col in inspector.get_columns(table_name)] + + def get_table_constraints(self, table_name: str) -> dict: + """Get constraints for a table (primary key, unique, foreign keys, checks). + + Args: + table_name: Table to inspect + + Returns: + Dictionary with constraint information + """ + with self.sync_engine.connect() as connection: + inspector = inspect(connection) + return { + "pk": inspector.get_pk_constraint(table_name), + "unique": inspector.get_unique_constraints(table_name), + "fk": inspector.get_foreign_keys(table_name), + "checks": inspector.get_check_constraints(table_name), + } + + def execute_sql(self, sql: str) -> list: + """Execute arbitrary SQL and return results. + + Args: + sql: SQL statement to execute + + Returns: + List of result rows + """ + with self.sync_engine.connect() as connection: + result = connection.execute(text(sql)) + return result.fetchall() + + +@pytest.fixture +def alembic_config() -> Config: + """Provide Alembic configuration for migration tests. + + Returns: + Configured Alembic Config object + """ + config = Config() + project_root: Path = Path(__file__).parents[2] # Navigate to backend/ + config.set_main_option("script_location", str(project_root / "alembic")) + config.set_main_option("sqlalchemy.url", settings.sync_database_url) + return config + + +@pytest.fixture +def migration_helper(alembic_config: Config) -> MigrationHelper: + """Provide migration testing helper. + + Returns: + MigrationHelper instance for testing migrations + """ + return MigrationHelper(alembic_config) diff --git a/backend/tests/integration/__init__.py b/backend/tests/integration/__init__.py new file mode 100644 index 0000000..c66cd71 --- /dev/null +++ b/backend/tests/integration/__init__.py @@ -0,0 +1 @@ +"""Integration tests package.""" diff --git a/backend/tests/integration/test_auth_models.py b/backend/tests/integration/test_auth_models.py new file mode 100644 index 0000000..fbcd178 --- /dev/null +++ b/backend/tests/integration/test_auth_models.py @@ -0,0 +1,488 @@ +"""Tests for authentication models and relationships. + +Tests validate User model creation, password handling, and ownership relationships. +""" + +from datetime import UTC, datetime, timedelta, timezone +from uuid import uuid4 + +import pytest +from pydantic import UUID4 +from sqlalchemy.exc import IntegrityError +from sqlmodel import select +from sqlmodel.ext.asyncio.session import AsyncSession + +from app.api.auth.models import Organization, OrganizationRole, User + + +@pytest.mark.unit +class TestUserModelBasics: + """Tests for basic User model functionality.""" + + def test_user_model_has_id_field(self): + """Verify User model has an id field.""" + assert hasattr(User, "id") + + def test_user_model_has_username_field(self): + """Verify User model has a username field.""" + assert hasattr(User, "username") + + def test_user_model_has_required_fields(self): + """Verify User model has required email and hashed_password fields.""" + # FastAPI-Users base class provides email and hashed_password + assert hasattr(User, "email") + assert hasattr(User, "hashed_password") + + def test_user_model_has_organization_relationship(self): + """Verify User model has organization relationship.""" + assert hasattr(User, "organization") + + def test_user_model_has_organization_id_foreign_key(self): + """Verify User model has organization_id foreign key.""" + assert hasattr(User, "organization_id") + + def test_user_model_has_organization_role(self): + """Verify User model has organization_role field.""" + assert hasattr(User, "organization_role") + + def test_user_model_has_products_relationship(self): + """Verify User model has products relationship.""" + assert hasattr(User, "products") + + def test_user_model_has_oauth_accounts(self): + """Verify User model has oauth_accounts relationship.""" + assert hasattr(User, "oauth_accounts") + + def test_user_model_has_timestamp_fields(self): + """Verify User model has created_at and updated_at fields.""" + assert hasattr(User, "created_at") + assert hasattr(User, "updated_at") + + def test_organization_role_enum_values(self): + """Verify OrganizationRole enum has correct values.""" + assert OrganizationRole.OWNER.value == "owner" + assert OrganizationRole.MEMBER.value == "member" + + +@pytest.mark.integration +class TestUserModelPersistence: + """Tests for persisting User model to database.""" + + @pytest.mark.asyncio + async def test_create_user_with_required_fields(self, session: AsyncSession): + """Verify creating user with required fields.""" + email = "test@example.com" + username = "testuser" + hashed_password = "hashed_password_value" + + user = User( + email=email, + username=username, + hashed_password=hashed_password, + ) + + session.add(user) + await session.commit() + await session.refresh(user) + + assert user.id is not None + assert user.email == email + assert user.username == username + assert user.created_at is not None + assert user.updated_at is not None + + @pytest.mark.asyncio + async def test_create_user_without_username(self, session: AsyncSession): + """Verify creating user without username is allowed.""" + email = "test@example.com" + hashed_password = "hashed_password_value" + + user = User( + email=email, + hashed_password=hashed_password, + ) + + session.add(user) + await session.commit() + await session.refresh(user) + + assert user.id is not None + assert user.email == email + assert user.username is None + + @pytest.mark.asyncio + async def test_user_password_stored_hashed(self, session: AsyncSession): + """Verify password is stored in hashed form.""" + email = "test@example.com" + hashed_password = "bcrypt_hashed_value$2b$12$..." + + user = User( + email=email, + hashed_password=hashed_password, + ) + + session.add(user) + await session.commit() + await session.refresh(user) + + # Password stored as provided (should be hashed by the application before creating) + assert user.hashed_password == hashed_password + + @pytest.mark.asyncio + async def test_user_defaults_organization_id_to_none(self, session: AsyncSession): + """Verify user organization_id defaults to None.""" + user = User( + email="test@example.com", + hashed_password="hashed", + ) + + session.add(user) + await session.commit() + await session.refresh(user) + + assert user.organization_id is None + assert user.organization is None + + @pytest.mark.asyncio + async def test_user_defaults_organization_role_to_none(self, session: AsyncSession): + """Verify user organization_role defaults to None.""" + user = User( + email="test@example.com", + hashed_password="hashed", + ) + + session.add(user) + await session.commit() + await session.refresh(user) + + assert user.organization_role is None + + +@pytest.mark.integration +class TestUserModelTimestamps: + """Tests for User model timestamp fields.""" + + @pytest.mark.asyncio + async def test_created_at_set_on_insert(self, session: AsyncSession): + """Verify created_at is set when user is created.""" + before_create = datetime.now(UTC) + + user = User( + email="test@example.com", + hashed_password="hashed", + ) + + session.add(user) + await session.commit() + await session.refresh(user) + + after_create = datetime.now(UTC) + + assert user.created_at is not None + assert before_create <= user.created_at <= after_create + + @pytest.mark.asyncio + async def test_updated_at_set_on_insert(self, session: AsyncSession): + """Verify updated_at is set when user is created.""" + before_create = datetime.now(UTC) + + user = User( + email="test@example.com", + hashed_password="hashed", + ) + + session.add(user) + await session.commit() + await session.refresh(user) + + after_create = datetime.now(UTC) + + assert user.updated_at is not None + assert before_create <= user.updated_at <= after_create + + @pytest.mark.asyncio + async def test_timestamps_are_equal_on_creation(self, session: AsyncSession): + """Verify created_at and updated_at are equal on creation.""" + user = User( + email="test@example.com", + hashed_password="hashed", + ) + + session.add(user) + await session.commit() + await session.refresh(user) + + # They should be very close (within 1 second) + assert abs((user.updated_at - user.created_at).total_seconds()) < 1 + + +@pytest.mark.integration +class TestUserQueryingAndRetrieval: + """Tests for querying and retrieving User models.""" + + @pytest.mark.asyncio + async def test_retrieve_user_by_id(self, session: AsyncSession): + """Verify user can be retrieved by ID.""" + user = User( + email="test@example.com", + hashed_password="hashed", + ) + + session.add(user) + await session.commit() + + # Create new session to test retrieval + statement = select(User).where(User.id == user.id) + result = await session.execute(statement) + retrieved = result.unique().scalar_one_or_none() + + assert retrieved is not None + assert retrieved.id == user.id + assert retrieved.email == user.email + + @pytest.mark.asyncio + async def test_retrieve_user_by_email(self, session: AsyncSession): + """Verify user can be retrieved by email.""" + email = "test@example.com" + user = User( + email=email, + hashed_password="hashed", + ) + + session.add(user) + await session.commit() + + statement = select(User).where(User.email == email) + result = await session.execute(statement) + retrieved = result.unique().scalar_one_or_none() + + assert retrieved is not None + assert retrieved.email == email + + @pytest.mark.asyncio + async def test_retrieve_user_by_username(self, session: AsyncSession): + """Verify user can be retrieved by username.""" + username = "testuser123" + user = User( + email="test@example.com", + username=username, + hashed_password="hashed", + ) + + session.add(user) + await session.commit() + + statement = select(User).where(User.username == username) + result = await session.execute(statement) + retrieved = result.unique().scalar_one_or_none() + + assert retrieved is not None + assert retrieved.username == username + + @pytest.mark.asyncio + async def test_nonexistent_user_returns_none(self, session: AsyncSession): + """Verify querying for nonexistent user returns None.""" + statement = select(User).where(User.email == "nonexistent@example.com") + result = await session.execute(statement) + retrieved = result.unique().scalar_one_or_none() + + assert retrieved is None + + @pytest.mark.asyncio + async def test_retrieve_multiple_users(self, session: AsyncSession): + """Verify multiple users can be retrieved.""" + users = [User(email=f"user{i}@example.com", hashed_password="hashed") for i in range(5)] + + for user in users: + session.add(user) + + await session.commit() + + statement = select(User) + result = await session.execute(statement) + retrieved = result.unique().scalars().all() + + assert len(retrieved) >= 5 + + +@pytest.mark.integration +class TestUserUniquenessConstraints: + """Tests for User model uniqueness constraints.""" + + @pytest.mark.asyncio + async def test_email_must_be_unique(self, session: AsyncSession): + """Verify email field is unique.""" + email = "unique@example.com" + + user1 = User(email=email, hashed_password="hashed1") + session.add(user1) + await session.commit() + + user2 = User(email=email, hashed_password="hashed2") + session.add(user2) + + from sqlalchemy.exc import IntegrityError + + with pytest.raises(IntegrityError): + await session.commit() + + @pytest.mark.asyncio + async def test_username_must_be_unique_when_provided(self, session: AsyncSession): + """Verify username field is unique when provided.""" + username = "uniqueuser" + + user1 = User( + email="user1@example.com", + username=username, + hashed_password="hashed1", + ) + session.add(user1) + await session.commit() + + user2 = User( + email="user2@example.com", + username=username, + hashed_password="hashed2", + ) + session.add(user2) + + from sqlalchemy.exc import IntegrityError + + with pytest.raises(IntegrityError): + await session.commit() + + @pytest.mark.asyncio + async def test_multiple_users_without_username_allowed(self, session: AsyncSession): + """Verify multiple users can have NULL username.""" + user1 = User( + email="user1@example.com", + hashed_password="hashed1", + ) + user2 = User( + email="user2@example.com", + hashed_password="hashed2", + ) + + session.add(user1) + session.add(user2) + + # Should not raise an error + await session.commit() + + # Verify both users were created + statement = select(User).where(User.username.is_(None)) + result = await session.execute(statement) + retrieved = result.unique().scalars().all() + + assert len(retrieved) >= 2 + + +@pytest.mark.integration +class TestUserOrganizationRelationship: + """Tests for User organization relationships.""" + + @pytest.mark.asyncio + async def test_user_can_be_assigned_to_organization(self, session: AsyncSession): + """Verify user can be assigned to an organization.""" + # Create an owner for the organization first + owner = User(email="owner@example.com", hashed_password="hashed") + session.add(owner) + await session.flush() + + org = Organization(name="Test Org", owner_id=owner.id) + session.add(org) + await session.flush() + + user = User( + email="test@example.com", + hashed_password="hashed", + organization_id=org.id, + organization_role=OrganizationRole.MEMBER, + ) + session.add(user) + await session.commit() + await session.refresh(user) + + assert user.organization_id == org.id + assert user.organization_role == OrganizationRole.MEMBER + + @pytest.mark.asyncio + async def test_user_owner_role(self, session: AsyncSession): + """Verify user can have owner role.""" + # Create an owner for the organization first + owner = User(email="owner@example.com", hashed_password="hashed") + session.add(owner) + await session.flush() + + org = Organization(name="Test Org", owner_id=owner.id) + session.add(org) + await session.flush() + + user = User( + email="test@example.com", + hashed_password="hashed", + organization_id=org.id, + organization_role=OrganizationRole.OWNER, + ) + session.add(user) + await session.commit() + await session.refresh(user) + + assert user.organization_role == OrganizationRole.OWNER + + @pytest.mark.asyncio + async def test_user_can_be_removed_from_organization(self, session: AsyncSession): + """Verify user can be removed from organization.""" + # Create an owner for the organization + owner = User(email="owner@example.com", hashed_password="hashed") + session.add(owner) + # Flush to get owner.id + await session.flush() + + org = Organization(name="Test Org", owner_id=owner.id) + session.add(org) + await session.flush() + + user = User( + email="test@example.com", + hashed_password="hashed", + organization_id=org.id, + organization_role=OrganizationRole.MEMBER, + ) + session.add(user) + await session.commit() + + # Remove from organization + user.organization_id = None + user.organization_role = None + session.add(user) + await session.commit() + await session.refresh(user) + + assert user.organization_id is None + assert user.organization_role is None + + +@pytest.mark.unit +class TestUserModelValidation: + """Tests for User model field validation.""" + + def test_user_email_is_required(self): + """Verify email is validated properly.""" + # SQLModel/SQLAlchemy validates the field definition + assert "email" in User.model_fields + + def test_user_password_is_required(self): + """Verify password is validated properly.""" + assert hasattr(User, "hashed_password") + + def test_organization_role_accepts_valid_enum_values(self): + """Verify organization_role enum values are correct.""" + valid_roles = [OrganizationRole.OWNER, OrganizationRole.MEMBER] + + assert all(isinstance(role, OrganizationRole) for role in valid_roles) + + def test_organization_role_string_values(self): + """Verify organization_role string values.""" + assert OrganizationRole.OWNER in [OrganizationRole.OWNER, OrganizationRole.MEMBER] + assert OrganizationRole.MEMBER in [OrganizationRole.OWNER, OrganizationRole.MEMBER] diff --git a/backend/tests/integration/test_background_data_models.py b/backend/tests/integration/test_background_data_models.py new file mode 100644 index 0000000..62c844d --- /dev/null +++ b/backend/tests/integration/test_background_data_models.py @@ -0,0 +1,289 @@ +"""Integration tests for background data models (with database).""" + +import pytest +from sqlalchemy.exc import IntegrityError +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.orm import selectinload +from sqlmodel import select + +from app.api.background_data.models import ( + Category, + CategoryMaterialLink, + CategoryProductTypeLink, + Material, + ProductType, + Taxonomy, + TaxonomyDomain, +) +from tests.fixtures.database import DBOperations + + +@pytest.mark.integration +class TestTaxonomyModel: + """Integration tests for Taxonomy model.""" + + async def test_create_taxonomy(self, session: AsyncSession): + """Test creating taxonomy in database.""" + taxonomy = Taxonomy( + name="Materials Taxonomy", + version="v1.0.0", + description="Test taxonomy", + domains={TaxonomyDomain.MATERIALS}, + source="https://example.com", + ) + session.add(taxonomy) + await session.flush() + await session.refresh(taxonomy) + + assert taxonomy.id is not None + assert taxonomy.name == "Materials Taxonomy" + assert taxonomy.created_at is not None + assert taxonomy.updated_at is not None + + async def test_taxonomy_str_representation(self, db_taxonomy: Taxonomy): + """Test Taxonomy __str__ method.""" + expected = f"{db_taxonomy.name} (id: {db_taxonomy.id})" + assert str(db_taxonomy) == expected + + async def test_taxonomy_with_multiple_domains(self, session: AsyncSession): + """Test taxonomy with multiple domains.""" + taxonomy = Taxonomy( + name="Multi-domain Taxonomy", + version="v1.0.0", + description="Test", + domains={TaxonomyDomain.MATERIALS, TaxonomyDomain.PRODUCTS}, + ) + session.add(taxonomy) + await session.flush() + await session.refresh(taxonomy) + + assert len(taxonomy.domains) == 2 + assert TaxonomyDomain.MATERIALS in taxonomy.domains + assert TaxonomyDomain.PRODUCTS in taxonomy.domains + + async def test_taxonomy_cascades_delete_categories(self, session: AsyncSession, db_taxonomy: Taxonomy): + """Test deleting taxonomy cascades to categories.""" + category = Category( + name="Test Category", + taxonomy_id=db_taxonomy.id, + ) + session.add(category) + await session.flush() + category_id = category.id + + # Delete taxonomy + await session.delete(db_taxonomy) + await session.flush() + + # Verify category was deleted + result = await session.get(Category, category_id) + assert result is None + + async def test_list_taxonomies(self, session: AsyncSession, db_ops: DBOperations): + """Test querying multiple taxonomies.""" + # Create multiple taxonomies + for i in range(3): + taxonomy = Taxonomy( + name=f"Taxonomy {i}", + version=f"v{i}.0.0", + domains={TaxonomyDomain.MATERIALS}, + ) + await db_ops.create(taxonomy) + + # Query all + taxonomies = await db_ops.get_all(Taxonomy) + assert len(taxonomies) >= 3 + + +@pytest.mark.integration +class TestCategoryModel: + """Integration tests for Category model.""" + + async def test_create_category(self, session: AsyncSession, db_taxonomy: Taxonomy): + """Test creating category in database.""" + category = Category( + name="Metals", + description="Metal materials", + external_id="EXT123", + taxonomy_id=db_taxonomy.id, + ) + session.add(category) + await session.flush() + await session.refresh(category) + + assert category.id is not None + assert category.name == "Metals" + assert category.external_id == "EXT123" + assert category.taxonomy_id == db_taxonomy.id + + async def test_category_requires_taxonomy(self, session: AsyncSession): + """Test category requires taxonomy_id (foreign key constraint).""" + category = Category(name="Invalid Category") + session.add(category) + + with pytest.raises(IntegrityError): + await session.flush() + + async def test_category_with_subcategories(self, session: AsyncSession, db_category: Category): + """Test self-referential relationship.""" + subcategory = Category( + name="Ferrous Metals", + description="Iron-based metals", + taxonomy_id=db_category.taxonomy_id, + supercategory_id=db_category.id, + ) + session.add(subcategory) + await session.flush() + await session.refresh(db_category) + await session.refresh(subcategory) + + assert subcategory.supercategory_id == db_category.id + assert len(db_category.subcategories) == 1 + assert db_category.subcategories[0].id == subcategory.id + + async def test_recursive_category_structure(self, session: AsyncSession, db_taxonomy: Taxonomy): + """Test multi-level category hierarchy.""" + # Create 3-level hierarchy: Metals -> Ferrous -> Steel + metals = Category(name="Metals", taxonomy_id=db_taxonomy.id) + session.add(metals) + await session.flush() + + ferrous = Category( + name="Ferrous", + taxonomy_id=db_taxonomy.id, + supercategory_id=metals.id, + ) + session.add(ferrous) + await session.flush() + + steel = Category( + name="Steel", + taxonomy_id=db_taxonomy.id, + supercategory_id=ferrous.id, + ) + session.add(steel) + await session.flush() + + # Verify structure + await session.refresh(metals) + assert len(metals.subcategories) == 1 + assert metals.subcategories[0].name == "Ferrous" + + +@pytest.mark.integration +class TestMaterialModel: + """Integration tests for Material model.""" + + async def test_create_material(self, session: AsyncSession): + """Test creating material in database.""" + material = Material( + name="Steel", + description="Iron-carbon alloy", + source="https://example.com/steel", + density_kg_m3=7850.0, + is_crm=False, + ) + session.add(material) + await session.flush() + await session.refresh(material) + + assert material.id is not None + assert material.name == "Steel" + assert material.density_kg_m3 == 7850.0 + + async def test_material_with_minimal_fields(self, session: AsyncSession): + """Test material with only required fields.""" + material = Material(name="Minimal Material") + session.add(material) + await session.flush() + await session.refresh(material) + + assert material.id is not None + assert material.description is None + assert material.density_kg_m3 is None + + +@pytest.mark.integration +class TestProductTypeModel: + """Integration tests for ProductType model.""" + + async def test_create_product_type(self, session: AsyncSession): + """Test creating product type in database.""" + product_type = ProductType( + name="Electronics", + description="Electronic products", + ) + session.add(product_type) + await session.flush() + await session.refresh(product_type) + + assert product_type.id is not None + assert product_type.name == "Electronics" + + +@pytest.mark.integration +class TestRelationships: + """Integration tests for model relationships.""" + + async def test_category_material_many_to_many( + self, session: AsyncSession, db_category: Category, db_material: Material + ): + """Test many-to-many relationship between Category and Material.""" + link = CategoryMaterialLink( + category_id=db_category.id, + material_id=db_material.id, + ) + session.add(link) + await session.flush() + + # Reload with relationships eagerly loaded + stmt = select(Category).where(Category.id == db_category.id).options(selectinload(Category.materials)) + result = await session.exec(stmt) + category = result.one() + + stmt = select(Material).where(Material.id == db_material.id).options(selectinload(Material.categories)) + result = await session.exec(stmt) + material = result.one() + + assert len(category.materials) == 1 + assert category.materials[0].id == db_material.id + assert len(material.categories) == 1 + assert material.categories[0].id == db_category.id + + async def test_category_product_type_many_to_many( + self, session: AsyncSession, db_category: Category, db_product_type: ProductType + ): + """Test many-to-many relationship between Category and ProductType.""" + link = CategoryProductTypeLink( + category_id=db_category.id, + product_type_id=db_product_type.id, + ) + session.add(link) + await session.flush() + + # Reload with relationships eagerly loaded + stmt = select(Category).where(Category.id == db_category.id).options(selectinload(Category.product_types)) + result = await session.exec(stmt) + category = result.one() + + assert len(category.product_types) == 1 + assert category.product_types[0].id == db_product_type.id + + async def test_taxonomy_categories_relationship(self, session: AsyncSession, db_taxonomy: Taxonomy): + """Test one-to-many relationship between Taxonomy and Categories.""" + # Create multiple categories + for i in range(3): + category = Category( + name=f"Category {i}", + taxonomy_id=db_taxonomy.id, + ) + session.add(category) + + await session.flush() + + # Reload with relationships eagerly loaded + stmt = select(Taxonomy).where(Taxonomy.id == db_taxonomy.id).options(selectinload(Taxonomy.categories)) + result = await session.exec(stmt) + taxonomy = result.one() + + assert len(taxonomy.categories) == 3 # 3 new categories created in this test diff --git a/backend/tests/integration/test_database_operations.py b/backend/tests/integration/test_database_operations.py new file mode 100644 index 0000000..14b6585 --- /dev/null +++ b/backend/tests/integration/test_database_operations.py @@ -0,0 +1,334 @@ +"""Integration tests for database operations and patterns. + +Tests database transactions, constraints, and isolation. +""" + +import pytest +from sqlalchemy.exc import IntegrityError +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.orm import selectinload +from sqlmodel import select + +from app.api.background_data.models import Category, Material, Taxonomy +from tests.fixtures.database import DBOperations + + +@pytest.mark.integration +class TestDatabaseTransactions: + """Test database transaction behavior and isolation.""" + + async def test_transaction_rollback_on_session_exit(self, session: AsyncSession): + """Test that changes roll back when session exits without commit.""" + # Create a material + material = Material( + name="Rollback Test Material", + description="This should be rolled back", + density_kg_m3=8000.0, + ) + session.add(material) + await session.flush() + material_id = material.id + + # In a fresh session, the material shouldn't exist + # (Because the first session will rollback when exiting fixture) + # This is verified implicitly by fixtures using nested transactions + + assert material_id is not None + + async def test_nested_transaction_isolation(self, session: AsyncSession, db_ops: DBOperations): + """Test that changes in one session don't affect another.""" + # Create first material + material1 = Material( + name="Material 1", + description="First material", + density_kg_m3=7850.0, + ) + await db_ops.create(material1) + + # Verify it exists + retrieved = await db_ops.get_by_filter(Material, name="Material 1") + assert retrieved is not None + assert retrieved.id == material1.id + + async def test_flush_vs_commit_behavior(self, session: AsyncSession): + """Test difference between flush and commit.""" + # Flush makes ID available but doesn't commit + material = Material( + name="Flush Test", + description="Test flush behavior", + density_kg_m3=8000.0, + ) + session.add(material) + await session.flush() + + # After flush, ID is available + assert material.id is not None + + # But this doesn't actually commit in test context + # (Our conftest mocks commit to only flush) + + async def test_refresh_after_write(self, session: AsyncSession): + """Test refreshing after write operations.""" + material = Material( + name="Refresh Test", + description="Test refresh", + density_kg_m3=8000.0, + ) + session.add(material) + await session.flush() + + # Refresh to ensure timestamps are populated + await session.refresh(material) + + assert material.created_at is not None + assert material.updated_at is not None + + +@pytest.mark.integration +class TestDatabaseConstraints: + """Test database constraints and integrity checks.""" + + # async def test_unique_constraint_violation(self, session: AsyncSession, db_ops: DBOperations): + # """Test that unique constraints are enforced.""" + # # Material name is not unique in model, so this test is invalid unless model changes. + # pass + + async def test_foreign_key_constraint(self, session: AsyncSession, db_taxonomy: Taxonomy): + """Test that foreign key constraints are enforced.""" + # Create category with valid taxonomy_id + category = Category( + name="Test Category", + description="A test category", + taxonomy_id=db_taxonomy.id, + ) + session.add(category) + await session.flush() + await session.refresh(category) + + # Verify relationship works + assert category.taxonomy_id == db_taxonomy.id + assert category.taxonomy == db_taxonomy + + async def test_null_constraint_enforcement(self, session: AsyncSession): + """Test that NOT NULL constraints are enforced.""" + # Try to create material without required name field + # (Depends on model definition - this is example pattern) + material = Material( + name="Valid Name", + # density_kg_m3 is required in model + density_kg_m3=7850.0, + ) + session.add(material) + await session.flush() + + assert material.id is not None + + +@pytest.mark.integration +class TestDatabaseQueries: + """Test various database query patterns.""" + + async def test_simple_select_all(self, session: AsyncSession, db_ops: DBOperations): + """Test selecting all records of a type.""" + # Create multiple materials + mat1 = Material(name="Mat1", density_kg_m3=7850.0) + mat2 = Material(name="Mat2", density_kg_m3=8000.0) + + for mat in [mat1, mat2]: + await db_ops.create(mat) + + # Query all + materials = await db_ops.get_all(Material) + assert len(materials) >= 2 + names = {m.name for m in materials} + assert "Mat1" in names + assert "Mat2" in names + + async def test_filtered_query(self, session: AsyncSession, db_ops: DBOperations): + """Test querying with filters.""" + # Create materials with different densities + heavy = Material(name="Heavy", density_kg_m3=10000.0) + light = Material(name="Light", density_kg_m3=2700.0) + + for mat in [heavy, light]: + await db_ops.create(mat) + + # Query with filter + materials = await db_ops.get_all(Material, name="Heavy") + assert len(materials) >= 1 + assert materials[0].density_kg_m3 == 10000.0 + + async def test_count_query(self, session: AsyncSession): + """Test counting records.""" + # Create multiple materials + for i in range(3): + material = Material( + name=f"Material {i}", + density_kg_m3=7850.0 + i * 100, + ) + session.add(material) + + await session.flush() + + # Count all materials + stmt = select(Material) + result = await session.execute(stmt) + materials = result.scalars().all() + + assert len(materials) >= 3 + + async def test_ordered_query(self, session: AsyncSession): + """Test query ordering.""" + # Create materials with different names + for name in ["Zebra", "Apple", "Banana"]: + material = Material( + name=name, + density_kg_m3=7850.0, + ) + session.add(material) + + await session.flush() + + # Query with ordering + stmt = select(Material).order_by(Material.name) + result = await session.execute(stmt) + materials = result.scalars().all() + + # Should be in alphabetical order + names = [m.name for m in materials if m.name in ["Zebra", "Apple", "Banana"]] + assert names == sorted(names) + + async def test_limited_query(self, session: AsyncSession): + """Test query with limit.""" + # Create multiple materials + for i in range(5): + material = Material( + name=f"Material {i}", + density_kg_m3=7850.0, + ) + session.add(material) + + await session.flush() + + # Query with limit + stmt = select(Material).limit(2) + result = await session.execute(stmt) + materials = result.scalars().all() + + assert len(materials) <= 2 + + +@pytest.mark.integration +class TestDatabaseRelationships: + """Test database relationship handling.""" + + async def test_one_to_many_relationship( + self, + session: AsyncSession, + db_taxonomy: Taxonomy, + db_ops: DBOperations, + ): + """Test one-to-many relationship (Taxonomy -> Categories).""" + # Create categories for taxonomy + cat1 = Category( + name="Category 1", + description="First category", + taxonomy_id=db_taxonomy.id, + ) + cat2 = Category( + name="Category 2", + description="Second category", + taxonomy_id=db_taxonomy.id, + ) + + for cat in [cat1, cat2]: + await db_ops.create(cat) + + # Verify relationship with explicit load + stmt = select(Taxonomy).where(Taxonomy.id == db_taxonomy.id).options(selectinload(Taxonomy.categories)) + result = await session.execute(stmt) + refreshed_taxonomy = result.scalar_one() + + assert len(refreshed_taxonomy.categories) == 2 + + async def test_relationship_cascade_delete_behavior( + self, + session: AsyncSession, + db_taxonomy: Taxonomy, + ): + """Test cascade delete behavior (model-dependent).""" + # Create category linked to taxonomy + category = Category( + name="Cascade Test Category", + description="Will be deleted with taxonomy", + taxonomy_id=db_taxonomy.id, + ) + session.add(category) + await session.flush() + category_id = category.id + + stmt = select(Taxonomy).where(Taxonomy.id == db_taxonomy.id).options(selectinload(Taxonomy.categories)) + result = await session.execute(stmt) + refreshed_taxonomy = result.scalar_one() + + assert len(refreshed_taxonomy.categories) >= 1 + + +@pytest.mark.integration +class TestDatabaseMutations: + """Test INSERT, UPDATE, DELETE operations.""" + + async def test_create_and_retrieve(self, session: AsyncSession, db_ops: DBOperations): + """Test creating and retrieving a record.""" + # Create + material = Material( + name="Test Material", + description="For testing", + density_kg_m3=7850.0, + ) + created = await db_ops.create(material) + + # Retrieve + retrieved = await db_ops.get_by_id(Material, created.id) + + assert retrieved is not None + assert retrieved.name == "Test Material" + + async def test_update_record(self, session: AsyncSession, db_ops: DBOperations): + """Test updating a record.""" + # Create + material = Material( + name="Original Name", + description="Original description", + density_kg_m3=7850.0, + ) + created = await db_ops.create(material) + + # Update + created.name = "Updated Name" + created.density_kg_m3 = 8000.0 + session.add(created) + await session.flush() + + # Verify update + retrieved = await db_ops.get_by_id(Material, created.id) + assert retrieved.name == "Updated Name" + assert retrieved.density_kg_m3 == 8000.0 + + async def test_delete_record(self, session: AsyncSession, db_ops: DBOperations): + """Test deleting a record.""" + # Create + material = Material( + name="To Delete", + description="Will be deleted", + density_kg_m3=7850.0, + ) + created = await db_ops.create(material) + created_id = created.id + + # Delete + await db_ops.delete(created) + + # Verify deletion + retrieved = await db_ops.get_by_id(Material, created_id) + assert retrieved is None diff --git a/backend/tests/test_main.py b/backend/tests/test_main.py deleted file mode 100644 index 7444eb1..0000000 --- a/backend/tests/test_main.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Main test module for the application.""" - -from typing import TYPE_CHECKING - -from fastapi.testclient import TestClient - -if TYPE_CHECKING: - from httpx import Response - - -def test_read_units(client: TestClient) -> None: - """Test the units endpoint.""" - response: Response = client.get("/units") - assert response.status_code == 200 - assert response.json() == ["kg", "g", "m", "cm"] - - -def test_read_items(client: TestClient) -> None: - """Test the items endpoint.""" - response: Response = client.get("/file-storage/videos") - assert response.status_code == 200 - assert response.json() == [] diff --git a/backend/tests/test_migrations.py b/backend/tests/test_migrations.py new file mode 100644 index 0000000..28ed8e1 --- /dev/null +++ b/backend/tests/test_migrations.py @@ -0,0 +1,29 @@ +"""Test that all Alembic migrations run successfully.""" + +import logging + +import pytest + +logger = logging.getLogger(__name__) + + +@pytest.mark.asyncio +async def test_migrations_upgrade_head(setup_test_database): + """Test that all migrations can be upgraded to head without error.""" + # If we've reached here, migrations have already run successfully + # in the setup_test_database fixture, so this is a sanity check pass + assert True, "All migrations completed successfully" + + +@pytest.mark.asyncio +async def test_migrations_downgrade_upgrade(): + """Test migration downgrade and upgrade cycle. + + This is optional and tests the migration reversibility. + Only run if your migrations support downgrade. + """ + # Note: This requires migrations to have downgrade functions + # Uncomment if you want to test reversibility + # alembic_cfg = get_alembic_config() + # command.downgrade(alembic_cfg, "-1") # Downgrade one migration + # command.upgrade(alembic_cfg, "+1") # Upgrade one migration diff --git a/backend/tests/unit/__init__.py b/backend/tests/unit/__init__.py new file mode 100644 index 0000000..ea3f8b9 --- /dev/null +++ b/backend/tests/unit/__init__.py @@ -0,0 +1 @@ +"""Unit tests package.""" diff --git a/backend/tests/unit/auth/test_auth_utils.py b/backend/tests/unit/auth/test_auth_utils.py new file mode 100644 index 0000000..7d1f1cc --- /dev/null +++ b/backend/tests/unit/auth/test_auth_utils.py @@ -0,0 +1,208 @@ +import asyncio +import contextlib +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest +from fastapi import Request +from fastapi_users.exceptions import InvalidPasswordException, UserAlreadyExists +from redis.exceptions import ConnectionError as RedisConnectionError + +from app.api.auth.models import User +from app.api.auth.schemas import UserCreate +from app.api.auth.utils.email_validation import EmailChecker +from app.api.auth.utils.programmatic_user_crud import create_user + + +@pytest.fixture +def mock_redis(): + return AsyncMock() + + +class TestEmailChecker: + async def test_init_without_redis(self): + """Test initialization without Redis client.""" + checker = EmailChecker(redis_client=None) + + with patch("app.api.auth.utils.email_validation.DefaultChecker") as MockDefaultChecker: + mock_checker_instance = AsyncMock() + MockDefaultChecker.return_value = mock_checker_instance + + await checker.initialize() + + MockDefaultChecker.assert_called_once() + assert checker.checker == mock_checker_instance + mock_checker_instance.init_redis.assert_not_called() + mock_checker_instance.fetch_temp_email_domains.assert_called_once() + + await checker.close() + + async def test_init_with_redis(self, mock_redis): + """Test initialization with Redis client.""" + checker = EmailChecker(redis_client=mock_redis) + + with patch("app.api.auth.utils.email_validation.DefaultChecker") as MockDefaultChecker: + mock_checker_instance = AsyncMock() + MockDefaultChecker.return_value = mock_checker_instance + + await checker.initialize() + + MockDefaultChecker.assert_called_once() + assert checker.checker == mock_checker_instance + mock_checker_instance.init_redis.assert_called_once() + mock_checker_instance.fetch_temp_email_domains.assert_called_once() + + await checker.close() + + async def test_refresh_domains_success(self, mock_redis): + """Test successful domain refresh.""" + checker = EmailChecker(redis_client=mock_redis) + checker.checker = AsyncMock() + + await checker._refresh_domains() + + checker.checker.fetch_temp_email_domains.assert_called_once() + + async def test_refresh_domains_failure(self, mock_redis): + """Test domain refresh failure handles exceptions gracefully.""" + checker = EmailChecker(redis_client=mock_redis) + checker.checker = AsyncMock() + checker.checker.fetch_temp_email_domains.side_effect = RuntimeError("Refresh failed") + + # Should not raise exception + await checker._refresh_domains() + + checker.checker.fetch_temp_email_domains.assert_called_once() + + async def test_is_disposable_true(self, mock_redis): + """Test identifying disposable email.""" + checker = EmailChecker(redis_client=mock_redis) + checker.checker = AsyncMock() + checker.checker.is_disposable.return_value = True + + result = await checker.is_disposable("test@temp-mail.org") + + assert result is True + checker.checker.is_disposable.assert_called_with("test@temp-mail.org") + + async def test_is_disposable_false(self, mock_redis): + """Test identifying non-disposable email.""" + checker = EmailChecker(redis_client=mock_redis) + checker.checker = AsyncMock() + checker.checker.is_disposable.return_value = False + + result = await checker.is_disposable("user@example.com") + + assert result is False + + async def test_is_disposable_error_fail_open(self, mock_redis): + """Test error handling during check returns False (fail open).""" + checker = EmailChecker(redis_client=mock_redis) + checker.checker = AsyncMock() + checker.checker.is_disposable.side_effect = RedisConnectionError("Redis down") + + # When check fails, we should allow registration (return False) + result = await checker.is_disposable("user@example.com") + + assert result is False + + async def test_is_disposable_not_initialized(self, mock_redis): + """Test check when checker is not initialized.""" + checker = EmailChecker(redis_client=mock_redis) + checker.checker = None + + result = await checker.is_disposable("user@example.com") + + assert result is False + + async def test_close_cancels_task(self, mock_redis): + """Test close cancels the refresh task.""" + checker = EmailChecker(redis_client=mock_redis) + + # Mock the task to be awaitable + # Create a Future and verify it works when awaited + mock_task = asyncio.Future() + mock_task.set_result(None) # It needs a result if awaited + mock_task.cancel = MagicMock() + + checker._refresh_task = mock_task + mock_checker = AsyncMock() + checker.checker = mock_checker + + await checker.close() + + mock_task.cancel.assert_called_once() + mock_checker.close_connections.assert_called_once() + + +class TestProgrammaticUserCrud: + @pytest.fixture + def user_create(self): + return UserCreate(email="test@example.com", password="password123") + + @pytest.fixture + def mock_user_manager(self): + return AsyncMock() + + @pytest.fixture + def mock_session(self): + return AsyncMock() + + async def test_create_user_success(self, mock_session, user_create, mock_user_manager): + """Test successful user creation.""" + expected_user = User(id="uid", email=user_create.email) + mock_user_manager.create.return_value = expected_user + + # Mock the context manager + mock_context = AsyncMock() + mock_context.__aenter__.return_value = mock_user_manager + mock_context.__aexit__.return_value = None + + with patch( + "app.api.auth.utils.programmatic_user_crud.get_chained_async_user_manager_context", + return_value=mock_context, + ): + user = await create_user(mock_session, user_create, send_registration_email=True) + + assert user == expected_user + mock_user_manager.create.assert_called_once() + + # Verify request state was set + call_kwargs = mock_user_manager.create.call_args.kwargs + assert "request" in call_kwargs + request = call_kwargs["request"] + assert isinstance(request, Request) + assert request.state.send_registration_email is True + + async def test_create_user_already_exists(self, mock_session, user_create, mock_user_manager): + """Test user creation when user already exists.""" + mock_user_manager.create.side_effect = UserAlreadyExists() + + mock_context = AsyncMock() + mock_context.__aenter__.return_value = mock_user_manager + mock_context.__aexit__.return_value = None + + with patch( + "app.api.auth.utils.programmatic_user_crud.get_chained_async_user_manager_context", + return_value=mock_context, + ): + with pytest.raises(UserAlreadyExists) as exc: + await create_user(mock_session, user_create) + + assert f"User with email {user_create.email} already exists" in str(exc.value) + + async def test_create_user_invalid_password(self, mock_session, user_create, mock_user_manager): + """Test user creation with invalid password.""" + mock_user_manager.create.side_effect = InvalidPasswordException(reason="Too short") + + mock_context = AsyncMock() + mock_context.__aenter__.return_value = mock_user_manager + mock_context.__aexit__.return_value = None + + with patch( + "app.api.auth.utils.programmatic_user_crud.get_chained_async_user_manager_context", + return_value=mock_context, + ): + with pytest.raises(InvalidPasswordException) as exc: + await create_user(mock_session, user_create) + + assert "Password is invalid: Too short" in str(exc.value) diff --git a/backend/tests/unit/background_data/test_background_data_crud.py b/backend/tests/unit/background_data/test_background_data_crud.py new file mode 100644 index 0000000..e44e912 --- /dev/null +++ b/backend/tests/unit/background_data/test_background_data_crud.py @@ -0,0 +1,131 @@ +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +from app.api.background_data.crud import validate_category_creation, validate_category_taxonomy_domains +from app.api.background_data.models import Category, Taxonomy, TaxonomyDomain + + +@pytest.fixture +def mock_session(): + return AsyncMock() + + +class TestCategoryValidation: + async def test_validate_category_creation_with_supercategory(self, mock_session): + """Test validation when supercategory is provided.""" + category_create = AsyncMock() + category_create.taxonomy_id = 99 # Should be ignored if supercategory provided + + super_category = Category(id=1, taxonomy_id=10, name="Super") + + with patch( + "app.api.background_data.crud.db_get_model_with_id_if_it_exists", return_value=super_category + ) as mock_get: + # Case 1: Matching taxonomy_id + result_id, result_cat = await validate_category_creation( + mock_session, category_create, taxonomy_id=10, supercategory_id=1 + ) + + assert result_id == 10 + assert result_cat == super_category + mock_get.assert_called_with(mock_session, Category, 1) + + async def test_validate_category_creation_supercategory_mismatch(self, mock_session): + """Test validation fails when supercategory taxonomy mismatches.""" + category_create = AsyncMock() + super_category = Category(id=1, taxonomy_id=10, name="Super") + + with patch("app.api.background_data.crud.db_get_model_with_id_if_it_exists", return_value=super_category): + # Case 2: Mismatched taxonomy_id + with pytest.raises(ValueError) as exc: + await validate_category_creation(mock_session, category_create, taxonomy_id=20, supercategory_id=1) + + assert "does not belong to taxonomy with id 20" in str(exc.value) + + async def test_validate_category_creation_top_level(self, mock_session): + """Test validation for top-level category info.""" + category_create = AsyncMock() + category_create.taxonomy_id = 10 + + mock_taxonomy = Taxonomy(id=10, name="Tax") + + with patch( + "app.api.background_data.crud.db_get_model_with_id_if_it_exists", return_value=mock_taxonomy + ) as mock_get: + result_id, result_cat = await validate_category_creation( + mock_session, category_create, taxonomy_id=None, supercategory_id=None + ) + + assert result_id == 10 + assert result_cat is None + mock_get.assert_called_with(mock_session, Taxonomy, 10) + + async def test_validate_category_creation_missing_taxonomy(self, mock_session): + """Test validation fails if no taxonomy ID for top-level.""" + category_create = AsyncMock() + category_create.taxonomy_id = None + + with pytest.raises(ValueError) as exc: + await validate_category_creation(mock_session, category_create, taxonomy_id=None, supercategory_id=None) + + assert "Taxonomy ID is required" in str(exc.value) + + +class TestTaxonomyDomainValidation: + async def test_validate_domains_success(self, mock_session): + """Test successful domain validation.""" + category_ids = {1, 2} + expected_domain = TaxonomyDomain.PRODUCTS + + # Mock DB response + cat1 = Category(id=1, taxonomy=Taxonomy(domains=[TaxonomyDomain.PRODUCTS])) + cat2 = Category(id=2, taxonomy=Taxonomy(domains=[TaxonomyDomain.PRODUCTS, TaxonomyDomain.MATERIALS])) + + # Use MagicMock for result so .all() is synchronous (but returns value) + mock_result = MagicMock() + mock_result.all.return_value = [cat1, cat2] + mock_session.exec.return_value = mock_result + + await validate_category_taxonomy_domains(mock_session, category_ids, expected_domain) + + # await db.exec(...) -> returns mock_result + # mock_result.all() -> returns [cat1, cat2] + # len() works on list + mock_session.exec.assert_called_once() + + async def test_validate_domains_missing_category(self, mock_session): + """Test validation fails when category is missing.""" + category_ids = {1, 2} + expected_domain = TaxonomyDomain.PRODUCTS + + # Only return one category + cat1 = Category(id=1, taxonomy=Taxonomy(domains=[TaxonomyDomain.PRODUCTS])) + + mock_result = MagicMock() + mock_result.all.return_value = [cat1] + mock_session.exec.return_value = mock_result + + with pytest.raises(ValueError) as exc: + await validate_category_taxonomy_domains(mock_session, category_ids, expected_domain) + + # Match fuzzy since set representation might differ + assert "not found" in str(exc.value) + assert "2" in str(exc.value) + + async def test_validate_domains_invalid_domain(self, mock_session): + """Test validation fails when category has wrong domain.""" + category_ids = {1} + expected_domain = TaxonomyDomain.PRODUCTS + + # Category has wrong domain + cat1 = Category(id=1, taxonomy=Taxonomy(domains=[TaxonomyDomain.MATERIALS])) + + mock_result = MagicMock() + mock_result.all.return_value = [cat1] + mock_session.exec.return_value = mock_result + + with pytest.raises(ValueError) as exc: + await validate_category_taxonomy_domains(mock_session, category_ids, expected_domain) + + assert "belong to taxonomies outside of domains" in str(exc.value) diff --git a/backend/tests/unit/data_collection/test_data_collection_crud.py b/backend/tests/unit/data_collection/test_data_collection_crud.py new file mode 100644 index 0000000..124fb4c --- /dev/null +++ b/backend/tests/unit/data_collection/test_data_collection_crud.py @@ -0,0 +1,121 @@ +from unittest.mock import AsyncMock, MagicMock, patch +from uuid import uuid4 + +import pytest + +from app.api.auth.models import User +from app.api.background_data.models import ProductType +from app.api.common.schemas.associations import MaterialProductLinkCreateWithinProductAndMaterial +from app.api.data_collection.crud import add_material_to_product, create_physical_properties, create_product +from app.api.data_collection.models import PhysicalProperties, Product +from app.api.data_collection.schemas import PhysicalPropertiesCreate, ProductCreateWithComponents + + +@pytest.fixture +def mock_session(): + session = AsyncMock() + # add and add_all are synchronous methods in SQLAlchemy + session.add = MagicMock() + session.add_all = MagicMock() + return session + + +class TestPhysicalPropertiesCrud: + async def test_create_physical_properties_success(self, mock_session): + """Test successful creation of physical properties.""" + product_id = 1 + props_create = PhysicalPropertiesCreate(weight_g=10.0, width_cm=5.0) + + # Mock product that exists and has no properties + product = Product(id=product_id, name="Test Product") + product.physical_properties = None + + with patch("app.api.data_collection.crud.db_get_model_with_id_if_it_exists", return_value=product) as mock_get: + result = await create_physical_properties(mock_session, props_create, product_id) + + assert isinstance(result, PhysicalProperties) + assert result.weight_g == 10.0 + assert result.product_id == product_id + + mock_session.add.assert_called_once() + mock_session.commit.assert_called_once() + mock_session.refresh.assert_called_once() + + async def test_create_physical_properties_already_exist(self, mock_session): + """Test error when product already has properties.""" + product_id = 1 + props_create = PhysicalPropertiesCreate(weight_g=10.0) + + # Mock product that already has properties + product = Product(id=product_id, name="Test Product") + product.physical_properties = PhysicalProperties(weight_g=5.0) + + with patch("app.api.data_collection.crud.db_get_model_with_id_if_it_exists", return_value=product): + with pytest.raises(ValueError) as exc: + await create_physical_properties(mock_session, props_create, product_id) + + assert "already has physical properties" in str(exc.value) + + +class TestProductCrud: + async def test_create_product_success(self, mock_session): + """Test successful product creation.""" + owner_id = uuid4() + # Product must have at least one material or component + product_create = ProductCreateWithComponents( + name="New Product", + product_type_id=1, + components=[], + bill_of_materials=[{"material_id": 1, "quantity": 1.0, "unit": "kg"}], + ) + + mock_type = ProductType(id=1, name="Type") + mock_user = User(id=owner_id, email="test@example.com") + + with patch("app.api.data_collection.crud.db_get_model_with_id_if_it_exists") as mock_get: + # Configure mock to return type then user + mock_get.side_effect = [mock_type, mock_user] + + # Use patch for material existence check as well + with patch("app.api.data_collection.crud.db_get_models_with_ids_if_they_exist"): + result = await create_product(mock_session, product_create, owner_id) + + assert isinstance(result, Product) + assert result.name == "New Product" + assert result.owner_id == owner_id + + mock_session.add.assert_called() + mock_session.commit.assert_called_once() + + async def test_add_material_to_product_success(self, mock_session): + """Test adding material to product.""" + product_id = 1 + material_id = 10 + link_create = MaterialProductLinkCreateWithinProductAndMaterial(quantity=5.0) + + db_product = Product(id=product_id, name="Product") + db_product.bill_of_materials = [] + + with ( + patch("app.api.data_collection.crud.db_get_model_with_id_if_it_exists", return_value=db_product), + patch("app.api.data_collection.crud.db_get_models_with_ids_if_they_exist"), + patch("app.api.data_collection.crud.add_materials_to_product") as mock_add_batch, + ): + expected_link = MagicMock() + mock_add_batch.return_value = [expected_link] + + result = await add_material_to_product( + mock_session, product_id, material_link=link_create, material_id=material_id + ) + + assert result == expected_link + mock_add_batch.assert_called_once() + + async def test_add_material_missing_id(self, mock_session): + """Test error when material ID is missing.""" + link_create = MaterialProductLinkCreateWithinProductAndMaterial(quantity=5.0) + + with pytest.raises(ValueError) as exc: + await add_material_to_product(mock_session, product_id=1, material_link=link_create, material_id=None) + + assert "Material ID is required" in str(exc.value) diff --git a/backend/tests/tests/emails/__init__.py b/backend/tests/unit/emails/__init__.py similarity index 100% rename from backend/tests/tests/emails/__init__.py rename to backend/tests/unit/emails/__init__.py diff --git a/backend/tests/tests/emails/test_programmatic_emails.py b/backend/tests/unit/emails/test_programmatic_emails.py similarity index 94% rename from backend/tests/tests/emails/test_programmatic_emails.py rename to backend/tests/unit/emails/test_programmatic_emails.py index 390a857..3cf8f11 100644 --- a/backend/tests/tests/emails/test_programmatic_emails.py +++ b/backend/tests/unit/emails/test_programmatic_emails.py @@ -20,6 +20,22 @@ fake = Faker() +@pytest.fixture +def email_data() -> dict[str, str]: + """Return common data for email tests.""" + return { + "email": fake.email(), + "username": fake.user_name(), + "token": fake.uuid4(), + } + + +@pytest.fixture +def mock_email_sender(mocker) -> AsyncMock: + """Mock the email sender.""" + return mocker.patch("app.api.auth.utils.programmatic_emails.fm.send_message", new_callable=AsyncMock) + + ### Token Link Generation Tests ### def test_generate_token_link_default_base_url() -> None: """Test token link generation with default base URL from core settings.""" @@ -61,7 +77,7 @@ def test_generate_token_link_with_trailing_slash() -> None: link = generate_token_link(token, route, base_url=base_url_with_slash) # Should not have double slashes - assert "//" not in link.replace("https://", "") # noqa: PLR2004 # Magic value for double slash + assert "//" not in link.split("://")[1] # Should still have the correct route assert urlparse(link).path == route diff --git a/backend/tests/unit/test_auth_exceptions.py b/backend/tests/unit/test_auth_exceptions.py new file mode 100644 index 0000000..255a3c5 --- /dev/null +++ b/backend/tests/unit/test_auth_exceptions.py @@ -0,0 +1,465 @@ +"""Tests for authentication exceptions module. + +Tests validate exception hierarchy, HTTP status codes, and message formatting. +""" + +from uuid import uuid4 + +import pytest +from fastapi import status +from pydantic import UUID4 + +from app.api.auth.exceptions import ( + AlreadyMemberError, + AuthCRUDError, + DisposableEmailError, + OrganizationHasMembersError, + OrganizationNameExistsError, + UserDoesNotOwnOrgError, + UserHasNoOrgError, + UserIsNotMemberError, + UserNameAlreadyExistsError, + UserOwnershipError, + UserOwnsOrgError, +) +from app.api.common.exceptions import APIError + + +@pytest.mark.unit +class TestAuthCRUDErrorHierarchy: + """Test the exception class hierarchy.""" + + def test_auth_crud_error_is_api_error(self): + """Verify AuthCRUDError inherits from APIError.""" + assert issubclass(AuthCRUDError, APIError) + + def test_user_name_already_exists_error_is_auth_crud_error(self): + """Verify UserNameAlreadyExistsError inherits from AuthCRUDError.""" + assert issubclass(UserNameAlreadyExistsError, AuthCRUDError) + + def test_already_member_error_is_auth_crud_error(self): + """Verify AlreadyMemberError inherits from AuthCRUDError.""" + assert issubclass(AlreadyMemberError, AuthCRUDError) + + def test_user_ownership_error_is_api_error_not_auth_crud(self): + """Verify UserOwnershipError inherits from APIError directly, not AuthCRUDError.""" + assert issubclass(UserOwnershipError, APIError) + assert not issubclass(UserOwnershipError, AuthCRUDError) + + +@pytest.mark.unit +class TestUserNameAlreadyExistsError: + """Tests for UserNameAlreadyExistsError.""" + + def test_http_status_code_is_409_conflict(self): + """Verify UserNameAlreadyExistsError has 409 Conflict status.""" + assert UserNameAlreadyExistsError.http_status_code == status.HTTP_409_CONFLICT + + def test_error_message_includes_username(self): + """Verify error message includes the duplicate username.""" + username = "duplicate_user" + error = UserNameAlreadyExistsError(username=username) + assert username in error.message + assert "already taken" in error.message.lower() + + def test_error_message_with_special_characters(self): + """Verify error message handles usernames with special characters.""" + username = "user@example.com" + error = UserNameAlreadyExistsError(username=username) + assert username in error.message + + def test_error_message_with_unicode_username(self): + """Verify error message handles unicode usernames.""" + username = "用户名" # Chinese characters + error = UserNameAlreadyExistsError(username=username) + assert username in error.message + + +@pytest.mark.unit +class TestAlreadyMemberError: + """Tests for AlreadyMemberError.""" + + def test_http_status_code_is_409_conflict(self): + """Verify AlreadyMemberError has 409 Conflict status.""" + assert AlreadyMemberError.http_status_code == status.HTTP_409_CONFLICT + + def test_error_message_without_user_id(self): + """Verify error message without user_id uses personal phrasing.""" + error = AlreadyMemberError() + assert "You already belong to an organization" in error.message + + def test_error_message_with_user_id(self): + """Verify error message includes user_id when provided.""" + user_id = uuid4() + error = AlreadyMemberError(user_id=user_id) + assert str(user_id) in error.message + assert "already belongs to an organization" in error.message + + def test_error_message_with_user_id_and_details(self): + """Verify error message includes both user_id and details.""" + user_id = uuid4() + details = "User is an active member" + error = AlreadyMemberError(user_id=user_id, details=details) + assert str(user_id) in error.message + assert details in error.message + + def test_error_message_with_details_only(self): + """Verify error message includes details without user_id.""" + details = "Additional context" + error = AlreadyMemberError(details=details) + assert "You already belong to an organization" in error.message + assert details in error.message + + +@pytest.mark.unit +class TestUserOwnsOrgError: + """Tests for UserOwnsOrgError.""" + + def test_http_status_code_is_409_conflict(self): + """Verify UserOwnsOrgError has 409 Conflict status.""" + assert UserOwnsOrgError.http_status_code == status.HTTP_409_CONFLICT + + def test_error_message_without_user_id(self): + """Verify error message without user_id uses personal phrasing.""" + error = UserOwnsOrgError() + assert "You own an organization" in error.message + + def test_error_message_with_user_id(self): + """Verify error message includes user_id when provided.""" + user_id = uuid4() + error = UserOwnsOrgError(user_id=user_id) + assert str(user_id) in error.message + assert "owns an organization" in error.message + + def test_error_message_with_user_id_and_details(self): + """Verify error message includes both user_id and details.""" + user_id = uuid4() + details = "User must transfer ownership" + error = UserOwnsOrgError(user_id=user_id, details=details) + assert str(user_id) in error.message + assert details in error.message + + +@pytest.mark.unit +class TestUserHasNoOrgError: + """Tests for UserHasNoOrgError.""" + + def test_http_status_code_is_404_not_found(self): + """Verify UserHasNoOrgError has 404 Not Found status.""" + assert UserHasNoOrgError.http_status_code == status.HTTP_404_NOT_FOUND + + def test_error_message_without_user_id(self): + """Verify error message without user_id uses personal phrasing.""" + error = UserHasNoOrgError() + assert "You do not belong to an organization" in error.message + + def test_error_message_with_user_id(self): + """Verify error message includes user_id when provided.""" + user_id = uuid4() + error = UserHasNoOrgError(user_id=user_id) + assert str(user_id) in error.message + assert "does not belong to an organization" in error.message + + def test_error_message_with_user_id_and_details(self): + """Verify error message includes both user_id and details.""" + user_id = uuid4() + details = "User needs to join first" + error = UserHasNoOrgError(user_id=user_id, details=details) + assert str(user_id) in error.message + assert details in error.message + + +@pytest.mark.unit +class TestUserIsNotMemberError: + """Tests for UserIsNotMemberError.""" + + def test_http_status_code_is_403_forbidden(self): + """Verify UserIsNotMemberError has 403 Forbidden status.""" + assert UserIsNotMemberError.http_status_code == status.HTTP_403_FORBIDDEN + + def test_error_message_without_ids(self): + """Verify error message without IDs uses personal phrasing.""" + error = UserIsNotMemberError() + assert "You do not belong to this organization" in error.message + + def test_error_message_with_user_id_only(self): + """Verify error message with user_id only.""" + user_id = uuid4() + error = UserIsNotMemberError(user_id=user_id) + assert str(user_id) in error.message + assert "does not belong to the organization" in error.message + + def test_error_message_with_organization_id_only(self): + """Verify error message with organization_id only uses generic message.""" + org_id = uuid4() + error = UserIsNotMemberError(organization_id=org_id) + # When only org_id is provided (no user_id), uses generic personal message + assert "You do not belong to this organization" in error.message + # org_id is only included in message if BOTH user_id and org_id are provided + assert str(org_id) not in error.message + + def test_error_message_with_both_ids(self): + """Verify error message with both user_id and organization_id.""" + user_id = uuid4() + org_id = uuid4() + error = UserIsNotMemberError(user_id=user_id, organization_id=org_id) + assert str(user_id) in error.message + assert str(org_id) in error.message + + def test_error_message_with_ids_and_details(self): + """Verify error message with all three parameters.""" + user_id = uuid4() + org_id = uuid4() + details = "Membership denied" + error = UserIsNotMemberError(user_id=user_id, organization_id=org_id, details=details) + assert str(user_id) in error.message + assert str(org_id) in error.message + assert details in error.message + + +@pytest.mark.unit +class TestUserDoesNotOwnOrgError: + """Tests for UserDoesNotOwnOrgError.""" + + def test_http_status_code_is_403_forbidden(self): + """Verify UserDoesNotOwnOrgError has 403 Forbidden status.""" + assert UserDoesNotOwnOrgError.http_status_code == status.HTTP_403_FORBIDDEN + + def test_error_message_without_user_id(self): + """Verify error message without user_id uses personal phrasing.""" + error = UserDoesNotOwnOrgError() + assert "You do not own an organization" in error.message + + def test_error_message_with_user_id(self): + """Verify error message includes user_id when provided.""" + user_id = uuid4() + error = UserDoesNotOwnOrgError(user_id=user_id) + assert str(user_id) in error.message + assert "does not own an organization" in error.message + + def test_error_message_with_user_id_and_details(self): + """Verify error message includes both user_id and details.""" + user_id = uuid4() + details = "Owner privileges required" + error = UserDoesNotOwnOrgError(user_id=user_id, details=details) + assert str(user_id) in error.message + assert details in error.message + + +@pytest.mark.unit +class TestOrganizationHasMembersError: + """Tests for OrganizationHasMembersError.""" + + def test_http_status_code_is_409_conflict(self): + """Verify OrganizationHasMembersError has 409 Conflict status.""" + assert OrganizationHasMembersError.http_status_code == status.HTTP_409_CONFLICT + + def test_error_message_without_organization_id(self): + """Verify error message without organization_id.""" + error = OrganizationHasMembersError() + assert "has members and cannot be deleted" in error.message + assert "Transfer ownership or remove members first" in error.message + + def test_error_message_with_organization_id(self): + """Verify error message includes organization_id when provided.""" + org_id = uuid4() + error = OrganizationHasMembersError(organization_id=org_id) + assert str(org_id) in error.message + assert "has members and cannot be deleted" in error.message + + def test_error_message_includes_remediation_guidance(self): + """Verify error message includes remediation steps.""" + error = OrganizationHasMembersError() + assert "Transfer ownership" in error.message or "remove members" in error.message + + +@pytest.mark.unit +class TestOrganizationNameExistsError: + """Tests for OrganizationNameExistsError.""" + + def test_http_status_code_is_409_conflict(self): + """Verify OrganizationNameExistsError has 409 Conflict status.""" + assert OrganizationNameExistsError.http_status_code == status.HTTP_409_CONFLICT + + def test_default_error_message(self): + """Verify default error message when no message provided.""" + error = OrganizationNameExistsError() + assert "Organization with this name already exists" in error.message + + def test_custom_error_message(self): + """Verify custom error message can be provided.""" + custom_msg = "Custom organization error" + error = OrganizationNameExistsError(msg=custom_msg) + assert custom_msg in error.message + + +@pytest.mark.unit +class TestUserOwnershipError: + """Tests for UserOwnershipError.""" + + def test_http_status_code_is_403_forbidden(self): + """Verify UserOwnershipError has 403 Forbidden status.""" + assert UserOwnershipError.http_status_code == status.HTTP_403_FORBIDDEN + + def test_error_message_includes_model_name(self): + """Verify error message includes the model name.""" + # Using a mock model type that has get_api_model_name method + from unittest.mock import Mock + + mock_model = Mock() + mock_model.get_api_model_name.return_value.name_capital = "TestModel" + + user_id = uuid4() + model_id = uuid4() + error = UserOwnershipError(model_type=mock_model, model_id=model_id, user_id=user_id) + + assert "TestModel" in error.message + assert str(user_id) in error.message + assert str(model_id) in error.message + + def test_error_message_includes_user_id(self): + """Verify error message includes user_id.""" + from unittest.mock import Mock + + mock_model = Mock() + mock_model.get_api_model_name.return_value.name_capital = "DataSet" + + user_id = uuid4() + model_id = uuid4() + error = UserOwnershipError(model_type=mock_model, model_id=model_id, user_id=user_id) + + assert str(user_id) in error.message + assert "does not own" in error.message.lower() + + def test_error_message_includes_model_id(self): + """Verify error message includes model_id.""" + from unittest.mock import Mock + + mock_model = Mock() + mock_model.get_api_model_name.return_value.name_capital = "Project" + + user_id = uuid4() + model_id = uuid4() + error = UserOwnershipError(model_type=mock_model, model_id=model_id, user_id=user_id) + + assert str(model_id) in error.message + + +@pytest.mark.unit +class TestDisposableEmailError: + """Tests for DisposableEmailError.""" + + def test_http_status_code_is_400_bad_request(self): + """Verify DisposableEmailError has 400 Bad Request status.""" + assert DisposableEmailError.http_status_code == status.HTTP_400_BAD_REQUEST + + def test_error_message_includes_email(self): + """Verify error message includes the disposable email address.""" + email = "temp@tempmail.com" + error = DisposableEmailError(email=email) + assert email in error.message + assert "disposable email" in error.message.lower() + + def test_error_message_with_various_email_formats(self): + """Verify error message handles various email formats.""" + emails = [ + "user@10minutemail.com", + "test@guerrillemail.com", + "name.surname@throwaway.email", + ] + for email in emails: + error = DisposableEmailError(email=email) + assert email in error.message + assert "not allowed" in error.message.lower() + + +@pytest.mark.unit +class TestExceptionInheritanceChain: + """Tests for verifying the complete exception inheritance chain.""" + + def test_all_auth_crud_errors_inherit_from_api_error(self): + """Verify all AuthCRUDError subclasses ultimately inherit from APIError.""" + crud_error_subclasses = [ + UserNameAlreadyExistsError, + AlreadyMemberError, + UserOwnsOrgError, + UserHasNoOrgError, + UserIsNotMemberError, + UserDoesNotOwnOrgError, + OrganizationHasMembersError, + OrganizationNameExistsError, + DisposableEmailError, + ] + + for error_class in crud_error_subclasses: + assert issubclass(error_class, APIError), f"{error_class.__name__} must inherit from APIError" + + def test_exception_can_be_caught_as_api_error(self): + """Verify exceptions can be caught as APIError.""" + try: + raise UserNameAlreadyExistsError(username="test") + except APIError: + pass # Expected + else: + pytest.fail("UserNameAlreadyExistsError should be catchable as APIError") + + def test_exception_can_be_caught_as_auth_crud_error(self): + """Verify AuthCRUDError subclasses can be caught as AuthCRUDError.""" + try: + raise UserNameAlreadyExistsError(username="test") + except AuthCRUDError: + pass # Expected + else: + pytest.fail("UserNameAlreadyExistsError should be catchable as AuthCRUDError") + + +@pytest.mark.unit +class TestExceptionStatusCodes: + """Tests for verifying all status codes are correctly set.""" + + def test_409_conflict_errors(self): + """Verify all 409 Conflict errors have correct status code.""" + conflict_errors = [ + UserNameAlreadyExistsError("test"), + AlreadyMemberError(), + UserOwnsOrgError(), + OrganizationHasMembersError(), + OrganizationNameExistsError(), + ] + + for error in conflict_errors: + assert error.http_status_code == status.HTTP_409_CONFLICT + + def test_403_forbidden_errors(self): + """Verify all 403 Forbidden errors have correct status code.""" + forbidden_errors = [ + UserIsNotMemberError(), + UserDoesNotOwnOrgError(), + ] + + for error in forbidden_errors: + assert error.http_status_code == status.HTTP_403_FORBIDDEN + + def test_404_not_found_errors(self): + """Verify all 404 Not Found errors have correct status code.""" + error = UserHasNoOrgError() + assert error.http_status_code == status.HTTP_404_NOT_FOUND + + def test_400_bad_request_errors(self): + """Verify all 400 Bad Request errors have correct status code.""" + error = DisposableEmailError(email="test@tempmail.com") + assert error.http_status_code == status.HTTP_400_BAD_REQUEST + + def test_403_ownership_error(self): + """Verify UserOwnershipError has 403 Forbidden status code.""" + from unittest.mock import Mock + + mock_model = Mock() + mock_model.get_api_model_name.return_value.name_capital = "TestModel" + + error = UserOwnershipError( + model_type=mock_model, + model_id=uuid4(), + user_id=uuid4(), + ) + assert error.http_status_code == status.HTTP_403_FORBIDDEN diff --git a/backend/tests/unit/test_background_data_schemas.py b/backend/tests/unit/test_background_data_schemas.py new file mode 100644 index 0000000..05f7707 --- /dev/null +++ b/backend/tests/unit/test_background_data_schemas.py @@ -0,0 +1,163 @@ +"""Unit tests for background data models (no database required).""" + +import pytest +from pydantic import ValidationError + +from app.api.background_data.models import TaxonomyDomain +from app.api.background_data.schemas import ( + CategoryCreate, + CategoryUpdate, + MaterialCreate, + MaterialUpdate, + ProductTypeCreate, + TaxonomyCreate, + TaxonomyUpdate, +) + + +@pytest.mark.unit +class TestTaxonomySchemas: + """Test Taxonomy schema validation.""" + + def test_taxonomy_create_valid(self): + """Test creating valid TaxonomyCreate schema.""" + data = { + "name": "Test Taxonomy", + "version": "v1.0.0", + "description": "A test taxonomy", + "domains": {"materials"}, + "source": "https://example.com", + } + schema = TaxonomyCreate(**data) + + assert schema.name == "Test Taxonomy" + assert schema.version == "v1.0.0" + assert schema.domains == {TaxonomyDomain.MATERIALS} + + def test_taxonomy_create_name_too_short(self): + """Test TaxonomyCreate rejects name that's too short.""" + with pytest.raises(ValidationError) as exc_info: + TaxonomyCreate( + name="A", # Too short + version="v1.0.0", + domains={"materials"}, + ) + + errors = exc_info.value.errors() + assert any(e["loc"][0] == "name" for e in errors) + + def test_taxonomy_create_multiple_domains(self): + """Test taxonomy with multiple domains.""" + schema = TaxonomyCreate( + name="Multi-domain Taxonomy", + version="v1.0.0", + domains={"materials", "products"}, + ) + + assert len(schema.domains) == 2 + assert TaxonomyDomain.MATERIALS in schema.domains + assert TaxonomyDomain.PRODUCTS in schema.domains + + def test_taxonomy_update_partial(self): + """Test TaxonomyUpdate with partial data.""" + schema = TaxonomyUpdate(name="Updated Name", domains={"materials"}) + + assert schema.name == "Updated Name" + assert schema.version is None + assert schema.description is None + + +@pytest.mark.unit +class TestCategorySchemas: + """Test Category schema validation.""" + + def test_category_create_valid(self): + """Test creating valid CategoryCreate schema.""" + schema = CategoryCreate( + name="Test Category", + description="A test category", + taxonomy_id=1, + ) + + assert schema.name == "Test Category" + assert schema.taxonomy_id == 1 + + def test_category_create_minimal(self): + """Test CategoryCreate with only required fields.""" + schema = CategoryCreate(name="Minimal Category") + + assert schema.name == "Minimal Category" + assert schema.taxonomy_id is None + + def test_category_update_partial(self): + """Test CategoryUpdate with partial data.""" + schema = CategoryUpdate(name="Updated Category") + + assert schema.name == "Updated Category" + assert schema.description is None + + +@pytest.mark.unit +class TestMaterialSchemas: + """Test Material schema validation.""" + + def test_material_create_valid(self): + """Test creating valid MaterialCreate schema.""" + schema = MaterialCreate( + name="Steel", + description="Iron-carbon alloy", + density_kg_m3=7850.0, + is_crm=False, + ) + + assert schema.name == "Steel" + assert schema.density_kg_m3 == 7850.0 + assert schema.is_crm is False + + def test_material_create_negative_density_fails(self): + """Test MaterialCreate rejects negative density.""" + with pytest.raises(ValidationError) as exc_info: + MaterialCreate( + name="Invalid Material", + density_kg_m3=-100.0, + ) + + errors = exc_info.value.errors() + assert any(e["loc"][0] == "density_kg_m3" for e in errors) + + def test_material_create_zero_density_fails(self): + """Test MaterialCreate rejects zero density.""" + with pytest.raises(ValidationError): + MaterialCreate( + name="Invalid Material", + density_kg_m3=0.0, + ) + + def test_material_update_partial(self): + """Test MaterialUpdate with partial data.""" + schema = MaterialUpdate(density_kg_m3=8000.0) + + assert schema.density_kg_m3 == 8000.0 + assert schema.name is None + + +@pytest.mark.unit +class TestProductTypeSchemas: + """Test ProductType schema validation.""" + + def test_product_type_create_valid(self): + """Test creating valid Product TypeCreate schema.""" + schema = ProductTypeCreate( + name="Electronics", + description="Electronic products", + ) + + assert schema.name == "Electronics" + assert schema.description == "Electronic products" + + def test_product_type_create_minimal(self): + """Test ProductTypeCreate with only name.""" + schema = ProductTypeCreate(name="Minimal") + + assert schema.name == "Minimal" + assert schema.description is None diff --git a/backend/tests/unit/test_common_utils.py b/backend/tests/unit/test_common_utils.py new file mode 100644 index 0000000..3fd9dfc --- /dev/null +++ b/backend/tests/unit/test_common_utils.py @@ -0,0 +1,252 @@ +"""Unit tests for common utilities. + +Tests utilities, helpers, and common functions (no database required). +Demonstrates pytest-mock usage for mocking external dependencies. +""" + +import pytest + +from app.api.common.exceptions import APIError + + +@pytest.mark.unit +class TestAPIError: + """Test custom API error exception.""" + + def test_api_error_creation_message_only(self): + """Test creating APIError with just a message.""" + error = APIError(message="Test error") + + assert error.message == "Test error" + assert error.details is None + assert str(error) == "Test error" + + def test_api_error_creation_with_details(self): + """Test creating APIError with message and details.""" + error = APIError( + message="Validation failed", + details="Field 'email' is invalid", + ) + + assert error.message == "Validation failed" + assert error.details == "Field 'email' is invalid" + + def test_api_error_default_status_code(self): + """Test default HTTP status code is 500.""" + error = APIError(message="Internal error") + assert error.http_status_code == 500 + + def test_api_error_inheritable(self): + """Test that APIError can be subclassed with custom status codes.""" + + class NotFoundError(APIError): + http_status_code = 404 + + error = NotFoundError(message="Resource not found") + assert error.http_status_code == 404 + + def test_api_error_is_exception(self): + """Test that APIError is a proper Exception subclass.""" + error = APIError(message="Test") + assert isinstance(error, Exception) + + with pytest.raises(APIError): + raise error + + +@pytest.mark.unit +class TestMockingExamples: + """Demonstrate pytest-mock usage for testing patterns.""" + + def test_mock_simple_function(self, mocker): + """Example: Mock a simple function.""" + # Create a mock object + mock_func = mocker.MagicMock(return_value=42) + + # Call the mock + result = mock_func(1, 2, 3) + + # Assertions + assert result == 42 + mock_func.assert_called_once_with(1, 2, 3) + + def test_mock_function_side_effects(self, mocker): + """Example: Mock with side effects (exceptions, sequences).""" + # Mock that raises an exception + mock_func = mocker.MagicMock(side_effect=ValueError("Invalid value")) + + with pytest.raises(ValueError, match="Invalid value"): + mock_func() + + def test_mock_function_call_count(self, mocker): + """Example: Verify mock was called specific number of times.""" + mock_func = mocker.MagicMock() + + # Call multiple times + mock_func() + mock_func() + mock_func() + + # Verify call count + assert mock_func.call_count == 3 + + def test_patch_module_import(self, mocker): + """Example: Patch imports to simulate external dependencies.""" + # Mock an external module + mock_module = mocker.MagicMock() + mocker.patch.dict("sys.modules", {"fake_module": mock_module}) + + # Now code importing fake_module would get the mock + assert mock_module is not None + + def test_patch_class_method(self, mocker): + """Example: Patch a method on a class.""" + + class MyClass: + def method(self): + return "original" + + obj = MyClass() + original_result = obj.method() + assert original_result == "original" + + # Patch the method + mocker.patch.object(MyClass, "method", return_value="mocked") + obj2 = MyClass() + assert obj2.method() == "mocked" + + def test_spy_function_call(self, mocker): + """Example: Spy on function calls (wrap without replacing).""" + + class ForSpying: + def method(self, x): + return x * 2 + + obj = ForSpying() + spy = mocker.spy(obj, "method") + + result = obj.method(10) + + assert result == 20 + spy.assert_called_once_with(10) + + +@pytest.mark.unit +class TestValidationPatterns: + """Test examples for validation logic that doesn't require database.""" + + def test_string_length_validation(self): + """Example: Test string length validation.""" + + def validate_name(name: str, min_length: int = 1, max_length: int = 255) -> str: + """Validate name length.""" + if not name or len(name) < min_length: + raise ValueError(f"Name must be at least {min_length} character(s)") + if len(name) > max_length: + raise ValueError(f"Name cannot exceed {max_length} characters") + return name + + # Happy path + assert validate_name("Test") == "Test" + + # Error cases + with pytest.raises(ValueError, match="must be at least"): + validate_name("") + + with pytest.raises(ValueError, match="cannot exceed"): + validate_name("a" * 300) + + def test_enum_validation(self): + """Example: Test enum validation.""" + from enum import Enum + + class Status(str, Enum): + ACTIVE = "active" + INACTIVE = "inactive" + PENDING = "pending" + + def validate_status(status: str) -> Status: + """Validate and return Status enum.""" + try: + return Status(status) + except ValueError: + raise ValueError(f"Invalid status: {status}") + + # Happy path + assert validate_status("active") == Status.ACTIVE + + # Error case + with pytest.raises(ValueError, match="Invalid status"): + validate_status("invalid") + + def test_type_validation(self): + """Example: Test type validation.""" + + def validate_port(port) -> int: + """Validate port number.""" + if not isinstance(port, int): + raise TypeError(f"Port must be int, got {type(port).__name__}") + if not 1 <= port <= 65535: + raise ValueError(f"Port must be 1-65535, got {port}") + return port + + # Happy path + assert validate_port(8000) == 8000 + + # Type error + with pytest.raises(TypeError): + validate_port("8000") + + # Value error + with pytest.raises(ValueError, match="Port must be 1-65535"): + validate_port(99999) + + +@pytest.mark.unit +class TestAsyncUtilityPatterns: + """Examples of testing async utilities with pytest-mock.""" + + @pytest.mark.asyncio + async def test_async_mock_example(self, mocker): + """Example: Mock async functions.""" + # Create an async mock + mock_async_func = mocker.AsyncMock(return_value="async result") + + # Call it + result = await mock_async_func() + + # Verify + assert result == "async result" + mock_async_func.assert_called_once() + + @pytest.mark.asyncio + async def test_async_mock_with_side_effect(self, mocker): + """Example: Async mock that raises exceptions.""" + # Create async mock that raises + mock_func = mocker.AsyncMock(side_effect=RuntimeError("Async error")) + + # Verify it raises + with pytest.raises(RuntimeError, match="Async error"): + await mock_func() + + @pytest.mark.asyncio + async def test_async_context_manager_mock(self, mocker): + """Example: Mock async context managers.""" + + class AsyncResource: + async def __aenter__(self): + return "resource" + + async def __aexit__(self, *args): + pass + + # Create mock context manager + mock_resource = mocker.MagicMock() + mock_resource.__aenter__ = mocker.AsyncMock(return_value="mocked_resource") + mock_resource.__aexit__ = mocker.AsyncMock(return_value=None) + + # Use it + async with mock_resource as resource: + assert resource == "mocked_resource" + + mock_resource.__aenter__.assert_called_once() diff --git a/backend/tests/unit/test_core_config.py b/backend/tests/unit/test_core_config.py new file mode 100644 index 0000000..c36bb1c --- /dev/null +++ b/backend/tests/unit/test_core_config.py @@ -0,0 +1,283 @@ +"""Unit tests for core configuration loading and validation. + +Tests configuration defaults, environment variable parsing, and validation. +""" + +import pytest +from pydantic import BaseModel, Field, ValidationError + + +@pytest.mark.unit +class TestConfigurationPatterns: + """Test patterns for configuration validation.""" + + def test_config_with_defaults(self): + """Test configuration with sensible defaults.""" + + class AppConfig(BaseModel): + """App configuration with defaults.""" + + debug: bool = False + log_level: str = "INFO" + database_url: str = "sqlite:///app.db" + max_connections: int = 10 + + # Create with defaults + config = AppConfig() + assert config.debug is False + assert config.log_level == "INFO" + assert config.database_url == "sqlite:///app.db" + + def test_config_override_defaults(self): + """Test overriding default configuration values.""" + + class AppConfig(BaseModel): + """App configuration.""" + + debug: bool = False + port: int = 8000 + + # Override defaults + config = AppConfig(debug=True, port=9000) + assert config.debug is True + assert config.port == 9000 + + def test_config_validation_constraints(self): + """Test configuration validation constraints.""" + + class DatabaseConfig(BaseModel): + """Database configuration with constraints.""" + + host: str + port: int = Field(ge=1, le=65535) # Port range validation + min_connections: int = Field(ge=1) + max_connections: int = Field(ge=1) + + # Valid config + config = DatabaseConfig( + host="localhost", + port=5432, + min_connections=5, + max_connections=20, + ) + assert config.port == 5432 + + # Invalid port + with pytest.raises(ValidationError) as exc_info: + DatabaseConfig( + host="localhost", + port=99999, # Out of range + min_connections=1, + max_connections=10, + ) + errors = exc_info.value.errors() + assert any(e["loc"][0] == "port" for e in errors) + + def test_config_required_fields(self): + """Test that required fields are enforced.""" + + class ApiConfig(BaseModel): + """API configuration with required fields.""" + + api_key: str # Required, no default + api_secret: str # Required + timeout: int = 30 # Optional with default + + # Missing required field + with pytest.raises(ValidationError) as exc_info: + ApiConfig(api_key="key123") # Missing api_secret + + errors = exc_info.value.errors() + assert any(e["loc"][0] == "api_secret" for e in errors) + + def test_config_optional_fields(self): + """Test optional fields with None defaults.""" + + class OptionalConfig(BaseModel): + """Configuration with optional fields.""" + + required_field: str + optional_field: str | None = None + optional_with_default: str | None = "default" + + # Can be created without optional fields + config = OptionalConfig(required_field="test") + assert config.optional_field is None + assert config.optional_with_default == "default" + + def test_config_computed_fields(self): + """Test computed/derived fields in configuration.""" + from pydantic import computed_field + + class UrlConfig(BaseModel): + """Configuration with computed URL field.""" + + protocol: str = "https" + host: str + port: int = 443 + + @computed_field + @property + def url(self) -> str: + """Compute full URL.""" + return f"{self.protocol}://{self.host}:{self.port}" + + config = UrlConfig(host="example.com") + assert config.url == "https://example.com:443" + + config2 = UrlConfig(protocol="http", host="localhost", port=8000) + assert config2.url == "http://localhost:8000" + + def test_config_field_validation(self): + """Test custom field validation logic.""" + + class PasswordConfig(BaseModel): + """Configuration with password validation.""" + + password: str = Field(min_length=8) + + def __init__(self, **data): + super().__init__(**data) + # Custom validation + if not any(c.isupper() for c in self.password): + raise ValueError("Password must contain uppercase letter") + + # Valid password + config = PasswordConfig(password="MyPassword123") + assert config.password == "MyPassword123" + + # Too short + with pytest.raises(ValidationError): + PasswordConfig(password="short") + + # No uppercase + with pytest.raises(ValueError, match="uppercase"): + PasswordConfig(password="mypassword123") + + def test_config_environment_like_parsing(self): + """Test configuration parsing from dict like environment variables.""" + + class EnvConfig(BaseModel): + """Parse config from environment-like dict.""" + + database_url: str + redis_host: str = "localhost" + redis_port: int = 6379 + + # Simulate environment variable dict + env_dict = { + "database_url": "postgresql://user:pass@localhost/db", + "redis_host": "redis.example.com", + "redis_port": "6380", # String in env, should convert to int + } + + config = EnvConfig(**env_dict) + assert config.database_url == "postgresql://user:pass@localhost/db" + assert config.redis_host == "redis.example.com" + assert config.redis_port == 6380 # Converted to int + + def test_config_mode_validation(self): + """Test configuration modes (development, staging, production).""" + + class ModeConfig(BaseModel): + """Configuration based on mode.""" + + mode: str = "development" + debug: bool = False + + def __init__(self, **data): + super().__init__(**data) + # Auto-set debug based on mode + if self.mode == "development": + self.debug = True + elif self.mode == "production": + self.debug = False + + dev_config = ModeConfig(mode="development") + assert dev_config.debug is True + + prod_config = ModeConfig(mode="production") + assert prod_config.debug is False + + +@pytest.mark.unit +class TestConfigurationEdgeCases: + """Test edge cases and error conditions in configuration.""" + + def test_config_type_coercion(self): + """Test automatic type coercion.""" + + class TypeConfig(BaseModel): + count: int + ratio: float + enabled: bool + + # String to int + config = TypeConfig(count="42", ratio="3.14", enabled="true") + assert config.count == 42 + assert isinstance(config.count, int) + assert config.ratio == 3.14 + assert config.enabled is True + + def test_config_empty_strings(self): + """Test handling of empty strings.""" + + class StringConfig(BaseModel): + required_string: str + optional_string: str | None = None + + # Empty string for required field is allowed (pydantic default) + config = StringConfig(required_string="") + assert config.required_string == "" + + def test_config_whitespace_handling(self): + """Test whitespace handling in configuration.""" + + class NameConfig(BaseModel): + name: str + + # Whitespace is preserved + config = NameConfig(name=" test ") + assert config.name == " test " + + def test_config_case_sensitivity(self): + """Test that config keys are case-sensitive by default.""" + + class CaseConfig(BaseModel): + DatabaseUrl: str + + # Exact case match works + config = CaseConfig(DatabaseUrl="postgres://localhost") + assert config.DatabaseUrl == "postgres://localhost" + + # Wrong case fails + with pytest.raises(ValidationError): + CaseConfig(databaseUrl="postgres://localhost") + + def test_config_extra_fields_ignored(self): + """Test behavior with extra fields.""" + + class StrictConfig(BaseModel): + model_config = {"extra": "ignore"} + + name: str + + # Extra fields are silently ignored + config = StrictConfig(name="test", extra_field="ignored") + assert config.name == "test" + assert not hasattr(config, "extra_field") + + def test_config_extra_fields_error(self): + """Test error on extra fields when configured to forbid.""" + + class StrictConfig(BaseModel): + model_config = {"extra": "forbid"} + + name: str + + # Extra fields cause ValidationError + with pytest.raises(ValidationError) as exc_info: + StrictConfig(name="test", extra_field="not allowed") + + errors = exc_info.value.errors() + assert any(e["type"] == "extra_forbidden" for e in errors) diff --git a/backend/tests/unit/test_data_collection_schemas.py b/backend/tests/unit/test_data_collection_schemas.py new file mode 100644 index 0000000..d57f582 --- /dev/null +++ b/backend/tests/unit/test_data_collection_schemas.py @@ -0,0 +1,539 @@ +"""Tests for data collection schema validation. + +Tests validate Pydantic schemas for creating, reading, and updating products, +physical properties, and circularity properties using ProductCreateBaseProduct +and related schemas. +""" + +from datetime import UTC, datetime, timedelta +from uuid import uuid4 + +import pytest +from pydantic import ValidationError + +from app.api.data_collection.schemas import ( + CircularityPropertiesCreate, + CircularityPropertiesRead, + CircularityPropertiesUpdate, + PhysicalPropertiesCreate, + PhysicalPropertiesRead, + PhysicalPropertiesUpdate, + ProductCreateBaseProduct, + ValidDateTime, + ensure_timezone, + not_too_old, +) + + +@pytest.mark.unit +class TestValidatorsCommon: + """Tests for common validators used across schemas.""" + + def test_ensure_timezone_with_aware_datetime(self): + """Verify ensure_timezone accepts timezone-aware datetime.""" + dt = datetime.now(UTC) + result = ensure_timezone(dt) + assert result == dt + assert result.tzinfo is not None + + def test_ensure_timezone_rejects_naive_datetime(self): + """Verify ensure_timezone rejects naive datetime.""" + dt = datetime.now() # No timezone + with pytest.raises(ValueError) as exc_info: + ensure_timezone(dt) + assert "timezone" in str(exc_info.value).lower() + + def test_not_too_old_recent_datetime(self): + """Verify not_too_old accepts recent datetime.""" + dt = datetime.now(UTC) - timedelta(days=30) + result = not_too_old(dt) + assert result == dt + + def test_not_too_old_rejects_old_datetime(self): + """Verify not_too_old rejects datetime older than 365 days.""" + dt = datetime.now(UTC) - timedelta(days=366) + with pytest.raises(ValueError) as exc_info: + not_too_old(dt) + assert "365" in str(exc_info.value) or "days" in str(exc_info.value).lower() + + def test_not_too_old_accepts_boundary_date(self): + """Verify not_too_old accepts datetime within 365 days.""" + # Use a date 364 days in the past (safely within boundary) + dt = datetime.now(UTC) - timedelta(days=364) + result = not_too_old(dt) + assert result == dt + + def test_not_too_old_with_custom_delta(self): + """Verify not_too_old respects custom time delta.""" + custom_delta = timedelta(days=30) + old_dt = datetime.now(UTC) - timedelta(days=61) + with pytest.raises(ValueError): + not_too_old(old_dt, time_delta=custom_delta) + + +@pytest.mark.unit +class TestPhysicalPropertiesCreate: + """Tests for PhysicalPropertiesCreate schema.""" + + def test_create_with_all_fields(self): + """Verify creating physical properties with all fields.""" + data = { + "weight_g": 20000.0, + "height_cm": 150.0, + "width_cm": 70.0, + "depth_cm": 50.0, + } + props = PhysicalPropertiesCreate(**data) + + assert props.weight_g == 20000.0 + assert props.height_cm == 150.0 + assert props.width_cm == 70.0 + assert props.depth_cm == 50.0 + + def test_create_with_partial_fields(self): + """Verify creating physical properties with only some fields.""" + data = {"weight_g": 5000.0} + props = PhysicalPropertiesCreate(**data) + + assert props.weight_g == 5000.0 + assert props.height_cm is None + + def test_create_with_no_fields(self): + """Verify creating physical properties with no fields.""" + props = PhysicalPropertiesCreate() + + assert props.weight_g is None + assert props.height_cm is None + + def test_weight_must_be_positive(self): + """Verify weight must be positive.""" + data = {"weight_g": -1000.0} + with pytest.raises(ValidationError): + PhysicalPropertiesCreate(**data) + + def test_height_must_be_positive(self): + """Verify height must be positive.""" + data = {"height_cm": 0.0} + with pytest.raises(ValidationError): + PhysicalPropertiesCreate(**data) + + def test_width_must_be_positive(self): + """Verify width must be positive.""" + data = {"width_cm": -100.0} + with pytest.raises(ValidationError): + PhysicalPropertiesCreate(**data) + + def test_depth_must_be_positive(self): + """Verify depth must be positive.""" + data = {"depth_cm": -50.0} + with pytest.raises(ValidationError): + PhysicalPropertiesCreate(**data) + + def test_fractional_dimensions(self): + """Verify fractional dimensions are accepted.""" + data = { + "height_cm": 10.5, + "width_cm": 20.75, + "depth_cm": 5.25, + } + props = PhysicalPropertiesCreate(**data) + + assert props.height_cm == 10.5 + assert props.width_cm == 20.75 + + +@pytest.mark.unit +class TestPhysicalPropertiesRead: + """Tests for PhysicalPropertiesRead schema.""" + + def test_read_with_all_fields(self): + """Verify read schema accepts all fields with id.""" + data = { + "id": 1, + "weight_g": 20000.0, + "height_cm": 150.0, + "width_cm": 70.0, + "depth_cm": 50.0, + "created_at": datetime.now(UTC), + "updated_at": datetime.now(UTC), + } + props = PhysicalPropertiesRead(**data) + + assert props.id == 1 + assert props.weight_g == 20000.0 + + def test_read_requires_id(self): + """Verify read schema requires id field.""" + data = { + "weight_g": 20000.0, + "created_at": datetime.now(UTC), + "updated_at": datetime.now(UTC), + } + with pytest.raises(ValidationError): + PhysicalPropertiesRead(**data) + + +@pytest.mark.unit +class TestPhysicalPropertiesUpdate: + """Tests for PhysicalPropertiesUpdate schema.""" + + def test_update_single_field(self): + """Verify updating single field.""" + data = {"weight_g": 15000.0} + props = PhysicalPropertiesUpdate(**data) + + assert props.weight_g == 15000.0 + assert props.height_cm is None + + def test_update_multiple_fields(self): + """Verify updating multiple fields.""" + data = { + "weight_g": 15000.0, + "height_cm": 120.0, + } + props = PhysicalPropertiesUpdate(**data) + + assert props.weight_g == 15000.0 + assert props.height_cm == 120.0 + + def test_update_no_fields(self): + """Verify updating with no fields is allowed.""" + props = PhysicalPropertiesUpdate() + + assert props.weight_g is None + + +@pytest.mark.unit +class TestCircularityPropertiesCreate: + """Tests for CircularityPropertiesCreate schema.""" + + def test_create_with_all_fields(self): + """Verify creating circularity properties with all fields.""" + data = { + "recyclability_observation": "Can be recycled", + "recyclability_comment": "Recyclable", + "recyclability_reference": "ISO 14040", + "repairability_observation": "Can be repaired", + "repairability_comment": "Repairable", + "repairability_reference": "ISO 20887", + "remanufacturability_observation": "Can be remanufactured", + "remanufacturability_comment": "Remanufacturable", + "remanufacturability_reference": "UNEP 2018", + } + props = CircularityPropertiesCreate(**data) + + assert props.recyclability_observation == "Can be recycled" + assert props.repairability_comment == "Repairable" + + def test_create_with_no_fields(self): + """Verify creating circularity properties with no fields.""" + props = CircularityPropertiesCreate() + + assert props.recyclability_observation is None + assert props.repairability_comment is None + + def test_observation_max_length_500(self): + """Verify observation fields max length is 500.""" + long_text = "a" * 501 + data = {"recyclability_observation": long_text} + + with pytest.raises(ValidationError): + CircularityPropertiesCreate(**data) + + def test_comment_max_length_100(self): + """Verify comment fields max length is 100.""" + long_text = "a" * 101 + data = {"recyclability_comment": long_text} + + with pytest.raises(ValidationError): + CircularityPropertiesCreate(**data) + + def test_observation_exact_max_length(self): + """Verify exactly at max length is accepted.""" + text_500 = "a" * 500 + data = {"recyclability_observation": text_500} + props = CircularityPropertiesCreate(**data) + + assert len(props.recyclability_observation) == 500 + + +@pytest.mark.unit +class TestCircularityPropertiesRead: + """Tests for CircularityPropertiesRead schema.""" + + def test_read_with_all_fields(self): + """Verify read schema accepts all fields.""" + data = { + "id": 1, + "recyclability_observation": "Recyclable", + "created_at": datetime.now(UTC), + "updated_at": datetime.now(UTC), + } + props = CircularityPropertiesRead(**data) + + assert props.id == 1 + assert props.recyclability_observation == "Recyclable" + + +@pytest.mark.unit +class TestCircularityPropertiesUpdate: + """Tests for CircularityPropertiesUpdate schema.""" + + def test_update_single_field(self): + """Verify updating single field.""" + data = {"recyclability_observation": "Updated"} + props = CircularityPropertiesUpdate(**data) + + assert props.recyclability_observation == "Updated" + + def test_update_no_fields(self): + """Verify updating with no fields is allowed.""" + props = CircularityPropertiesUpdate() + + assert props.recyclability_observation is None + + +@pytest.mark.unit +class TestProductCreateBaseProductSchema: + """Tests for ProductCreateBaseProduct schema.""" + + def test_create_with_required_fields(self): + """Verify creating product with required fields.""" + data = {"name": "Test Product"} + product = ProductCreateBaseProduct(**data) + + assert product.name == "Test Product" + + def test_name_min_length_2(self): + """Verify product name must be at least 2 characters.""" + data = {"name": "A"} + with pytest.raises(ValidationError): + ProductCreateBaseProduct(**data) + + def test_name_max_length_100(self): + """Verify product name max length is 100.""" + long_name = "a" * 101 + data = {"name": long_name} + with pytest.raises(ValidationError): + ProductCreateBaseProduct(**data) + + def test_create_with_optional_fields(self): + """Verify creating product with optional fields.""" + data = { + "name": "Test Product", + "description": "A test product", + "brand": "TestBrand", + "model": "Model X", + } + product = ProductCreateBaseProduct(**data) + + assert product.description == "A test product" + assert product.brand == "TestBrand" + + def test_description_max_length(self): + """Verify description max length is 500.""" + long_desc = "a" * 501 + data = {"name": "Test", "description": long_desc} + with pytest.raises(ValidationError): + ProductCreateBaseProduct(**data) + + def test_brand_max_length(self): + """Verify brand max length is 100.""" + long_brand = "a" * 101 + data = {"name": "Test", "brand": long_brand} + with pytest.raises(ValidationError): + ProductCreateBaseProduct(**data) + + def test_model_max_length(self): + """Verify model max length is 100.""" + long_model = "a" * 101 + data = {"name": "Test", "model": long_model} + with pytest.raises(ValidationError): + ProductCreateBaseProduct(**data) + + def test_dismantling_notes_max_length(self): + """Verify dismantling notes max length is 500.""" + long_notes = "a" * 501 + data = {"name": "Test", "dismantling_notes": long_notes} + with pytest.raises(ValidationError): + ProductCreateBaseProduct(**data) + + def test_dismantling_time_start_validation(self): + """Verify dismantling_time_start must be in past.""" + future_time = datetime.now(UTC) + timedelta(days=1) + data = {"name": "Test", "dismantling_time_start": future_time} + with pytest.raises(ValidationError): + ProductCreateBaseProduct(**data) + + def test_dismantling_time_end_after_start(self): + """Verify dismantling_time_end must be after dismantling_time_start.""" + start_time = datetime.now(UTC) - timedelta(hours=2) + end_time = start_time - timedelta(hours=1) + data = { + "name": "Test", + "dismantling_time_start": start_time, + "dismantling_time_end": end_time, + } + with pytest.raises(ValidationError): + ProductCreateBaseProduct(**data) + + def test_name_with_special_characters(self): + """Verify product name accepts special characters.""" + data = {"name": "Test-Product_#1 (v2.0)"} + product = ProductCreateBaseProduct(**data) + + assert product.name == "Test-Product_#1 (v2.0)" + + def test_name_with_unicode(self): + """Verify product name accepts unicode characters.""" + data = {"name": "产品名称 Product 製品"} + product = ProductCreateBaseProduct(**data) + + assert "产品" in product.name + + def test_create_with_physical_properties(self): + """Verify creating product with physical properties.""" + data = { + "name": "Product with Props", + "physical_properties": { + "weight_g": 5000.0, + "height_cm": 100.0, + }, + } + product = ProductCreateBaseProduct(**data) + + assert product.physical_properties is not None + assert product.physical_properties.weight_g == 5000.0 + + def test_create_with_circularity_properties(self): + """Verify creating product with circularity properties.""" + data = { + "name": "Product", + "circularity_properties": { + "recyclability_observation": "Highly recyclable", + }, + } + product = ProductCreateBaseProduct(**data) + + assert product.circularity_properties is not None + assert "recyclable" in product.circularity_properties.recyclability_observation.lower() + + def test_create_with_product_type(self): + """Verify creating product with product_type_id.""" + data = { + "name": "Product", + "product_type_id": 123, + } + product = ProductCreateBaseProduct(**data) + + assert product.product_type_id == 123 + + def test_videos_default_to_empty_list(self): + """Verify videos default to empty list.""" + data = {"name": "Product"} + product = ProductCreateBaseProduct(**data) + + assert product.videos == [] + + def test_bill_of_materials_default_to_empty_list(self): + """Verify bill_of_materials default to empty list.""" + data = {"name": "Product"} + product = ProductCreateBaseProduct(**data) + + assert product.bill_of_materials == [] + + +@pytest.mark.unit +class TestValidDatetimeType: + """Tests for ValidDateTime custom type.""" + + def test_valid_recent_past_datetime(self): + """Verify ValidDateTime accepts recent past datetime.""" + dt = datetime.now(UTC) - timedelta(days=30) + from pydantic import BaseModel + + class TestModel(BaseModel): + event_time: ValidDateTime + + model = TestModel(event_time=dt) + assert model.event_time == dt + + def test_valid_datetime_rejects_future(self): + """Verify ValidDateTime rejects future datetime.""" + dt = datetime.now(UTC) + timedelta(hours=1) + from pydantic import BaseModel + + class TestModel(BaseModel): + event_time: ValidDateTime + + with pytest.raises(ValidationError): + TestModel(event_time=dt) + + def test_valid_datetime_requires_timezone(self): + """Verify ValidDateTime requires timezone-aware datetime.""" + dt = datetime.now() # Naive datetime + from pydantic import BaseModel + + class TestModel(BaseModel): + event_time: ValidDateTime + + with pytest.raises(ValidationError): + TestModel(event_time=dt) + + def test_valid_datetime_rejects_too_old(self): + """Verify ValidDateTime rejects datetime older than 365 days.""" + dt = datetime.now(UTC) - timedelta(days=400) + from pydantic import BaseModel + + class TestModel(BaseModel): + event_time: ValidDateTime + + with pytest.raises(ValidationError): + TestModel(event_time=dt) + + +@pytest.mark.unit +class TestSchemaEdgeCases: + """Tests for schema edge cases and boundary conditions.""" + + def test_zero_weight_rejected(self): + """Verify zero weight is rejected.""" + data = {"weight_g": 0.0} + with pytest.raises(ValidationError): + PhysicalPropertiesCreate(**data) + + def test_negative_dimensions_rejected(self): + """Verify negative dimensions are rejected.""" + for field in ["height_cm", "width_cm", "depth_cm"]: + data = {field: -10.0} + with pytest.raises(ValidationError): + PhysicalPropertiesCreate(**data) + + def test_large_weight_values(self): + """Verify large weight values are accepted.""" + data = {"weight_g": 1000000.0} # 1 mega-gram + props = PhysicalPropertiesCreate(**data) + assert props.weight_g == 1000000.0 + + def test_large_dimension_values(self): + """Verify large dimension values are accepted.""" + data = { + "height_cm": 100000.0, + "width_cm": 50000.0, + "depth_cm": 25000.0, + } + props = PhysicalPropertiesCreate(**data) + assert props.height_cm == 100000.0 + + def test_mixed_optional_required_fields(self): + """Verify mixing optional and required fields.""" + data = { + "name": "Product", + "description": None, + "brand": "BrandName", + "model": None, + } + product = ProductCreateBaseProduct(**data) + + assert product.brand == "BrandName" + assert product.model is None diff --git a/backend/tests/unit/test_ownership_validation.py b/backend/tests/unit/test_ownership_validation.py new file mode 100644 index 0000000..408e836 --- /dev/null +++ b/backend/tests/unit/test_ownership_validation.py @@ -0,0 +1,530 @@ +"""Tests for user ownership validation utilities. + +Tests validate that get_user_owned_object correctly enforces user ownership +and raises appropriate exceptions when access is denied. +""" + +from unittest.mock import AsyncMock, MagicMock, patch +from uuid import uuid4 + +import pytest +from pydantic import UUID4 +from sqlmodel.ext.asyncio.session import AsyncSession + +from app.api.auth.exceptions import UserOwnershipError +from app.api.auth.models import User +from app.api.common.crud.exceptions import DependentModelOwnershipError +from app.api.common.utils.ownership import get_user_owned_object + + +@pytest.mark.unit +class TestGetUserOwnedObjectSuccess: + """Tests for successful get_user_owned_object calls.""" + + @pytest.mark.asyncio + async def test_returns_object_when_user_owns_it(self, mocker): + """Verify function returns object when user owns it.""" + user_id = uuid4() + model_id = uuid4() + expected_object = MagicMock() + + # Mock the get_nested_model_by_id to return the object + mock_get_nested = mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + return_value=expected_object, + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + + result = await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + + assert result == expected_object + mock_get_nested.assert_called_once() + + @pytest.mark.asyncio + async def test_passes_correct_parameters_to_get_nested_model(self, mocker): + """Verify correct parameters are passed to get_nested_model_by_id.""" + user_id = uuid4() + model_id = uuid4() + expected_object = MagicMock() + + mock_get_nested = mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + return_value=expected_object, + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + + # Verify call with default user_fk="owner_id" + call_args = mock_get_nested.call_args + assert call_args.kwargs["parent_model"] == User + assert call_args.kwargs["parent_id"] == user_id + assert call_args.kwargs["dependent_model"] == mock_model + assert call_args.kwargs["dependent_id"] == model_id + assert call_args.kwargs["parent_fk_name"] == "owner_id" + + @pytest.mark.asyncio + async def test_uses_custom_user_fk_parameter(self, mocker): + """Verify custom user_fk parameter is passed through.""" + user_id = uuid4() + model_id = uuid4() + custom_fk = "custom_owner_field" + expected_object = MagicMock() + + mock_get_nested = mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + return_value=expected_object, + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + user_fk=custom_fk, + ) + + call_args = mock_get_nested.call_args + assert call_args.kwargs["parent_fk_name"] == custom_fk + + +@pytest.mark.unit +class TestGetUserOwnedObjectFailure: + """Tests for get_user_owned_object error handling.""" + + @pytest.mark.asyncio + async def test_raises_user_ownership_error_on_dependent_model_error(self, mocker): + """Verify UserOwnershipError is raised when DependentModelOwnershipError occurs.""" + user_id = uuid4() + model_id = uuid4() + + # Mock get_nested_model_by_id to raise DependentModelOwnershipError + mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + side_effect=DependentModelOwnershipError( + dependent_model=MagicMock(), + dependent_id=model_id, + parent_model=User, + parent_id=user_id, + ), + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + mock_model.get_api_model_name.return_value.name_capital = "TestModel" + + with pytest.raises(UserOwnershipError) as exc_info: + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + + error = exc_info.value + assert error.http_status_code == 403 + assert str(user_id) in error.message + assert str(model_id) in error.message + + @pytest.mark.asyncio + async def test_error_message_contains_model_name(self, mocker): + """Verify error message includes the model name.""" + user_id = uuid4() + model_id = uuid4() + + mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + side_effect=DependentModelOwnershipError( + dependent_model=MagicMock(), + dependent_id=model_id, + parent_model=User, + parent_id=user_id, + ), + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + model_name = "DataCollection" + mock_model.get_api_model_name.return_value.name_capital = model_name + + with pytest.raises(UserOwnershipError) as exc_info: + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + + assert model_name in exc_info.value.message + + @pytest.mark.asyncio + async def test_error_contains_forbidden_status_code(self, mocker): + """Verify UserOwnershipError has 403 Forbidden status code.""" + user_id = uuid4() + model_id = uuid4() + + mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + side_effect=DependentModelOwnershipError( + dependent_model=MagicMock(), + dependent_id=model_id, + parent_model=User, + parent_id=user_id, + ), + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + mock_model.get_api_model_name.return_value.name_capital = "Model" + + with pytest.raises(UserOwnershipError) as exc_info: + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + + assert exc_info.value.http_status_code == 403 + + +@pytest.mark.unit +class TestGetUserOwnedObjectParameterVariations: + """Tests for various parameter combinations.""" + + @pytest.mark.asyncio + async def test_with_uuid4_ids(self, mocker): + """Verify function works with various UUID4 IDs.""" + uuid_ids = [uuid4() for _ in range(3)] + + mock_get_nested = mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + return_value=MagicMock(), + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + + for user_id in uuid_ids: + for model_id in uuid_ids: + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + + assert mock_get_nested.call_count == len(uuid_ids) ** 2 + + @pytest.mark.asyncio + async def test_with_integer_model_id(self, mocker): + """Verify function works with integer model IDs.""" + user_id = uuid4() + model_id = 12345 + + mock_get_nested = mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + return_value=MagicMock(), + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + + call_args = mock_get_nested.call_args + assert call_args.kwargs["dependent_id"] == model_id + + @pytest.mark.asyncio + async def test_with_string_user_fk(self, mocker): + """Verify function works with different string user_fk values.""" + user_id = uuid4() + model_id = uuid4() + fk_values = ["owner_id", "created_by_id", "responsible_user_id", "author_id"] + + mock_get_nested = mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + return_value=MagicMock(), + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + + for fk_name in fk_values: + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + user_fk=fk_name, + ) + + assert mock_get_nested.call_count == len(fk_values) + + # Verify each call used different user_fk + for i, fk_name in enumerate(fk_values): + call_args = mock_get_nested.call_args_list[i] + assert call_args.kwargs["parent_fk_name"] == fk_name + + +@pytest.mark.unit +class TestGetUserOwnedObjectIntegration: + """Tests for integration aspects of ownership validation.""" + + @pytest.mark.asyncio + async def test_chain_of_responsibility_flow(self, mocker): + """Verify correct flow: valid object -> returned, invalid -> UserOwnershipError.""" + user_id = uuid4() + model_id = uuid4() + expected_object = MagicMock() + + mock_get_nested = mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + mock_model.get_api_model_name.return_value.name_capital = "TestResource" + + # First call: valid ownership + mock_get_nested.return_value = expected_object + result = await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + assert result == expected_object + + # Second call: invalid ownership + mock_get_nested.side_effect = DependentModelOwnershipError( + dependent_model=mock_model, + dependent_id=model_id, + parent_model=User, + parent_id=user_id, + ) + + with pytest.raises(UserOwnershipError): + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + + @pytest.mark.asyncio + async def test_preserves_exception_chain(self, mocker): + """Verify exception chain suppression with 'from None'.""" + user_id = uuid4() + model_id = uuid4() + + original_error = DependentModelOwnershipError( + dependent_model=MagicMock(), + dependent_id=model_id, + parent_model=User, + parent_id=user_id, + ) + + mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + side_effect=original_error, + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + mock_model.get_api_model_name.return_value.name_capital = "ModelName" + + with pytest.raises(UserOwnershipError) as exc_info: + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + + # The exception should have __cause__ set to None (from None) + assert exc_info.value.__cause__ is None + assert exc_info.value.__context__ is original_error + + @pytest.mark.asyncio + async def test_async_context_is_maintained(self, mocker): + """Verify async execution context is maintained.""" + user_id = uuid4() + model_id = uuid4() + + async_call_counter = AsyncMock(return_value=None) + + async def mock_get_nested(*args, **kwargs): + await async_call_counter() + return MagicMock() + + mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + side_effect=mock_get_nested, + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + + async_call_counter.assert_called_once() + + @pytest.mark.asyncio + async def test_database_session_not_modified(self, mocker): + """Verify database session is passed through without modification.""" + user_id = uuid4() + model_id = uuid4() + + mock_get_nested = mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + return_value=MagicMock(), + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + + # Verify the exact same db instance was passed + call_args = mock_get_nested.call_args + assert call_args.kwargs["db"] is db + + +@pytest.mark.unit +class TestGetUserOwnedObjectEdgeCases: + """Tests for edge cases and boundary conditions.""" + + @pytest.mark.asyncio + async def test_with_many_consecutive_calls(self, mocker): + """Verify function handles many consecutive calls correctly.""" + mock_get_nested = mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + return_value=MagicMock(), + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + + for _ in range(100): + user_id = uuid4() + model_id = uuid4() + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + + assert mock_get_nested.call_count == 100 + + @pytest.mark.asyncio + async def test_error_on_first_call(self, mocker): + """Verify error handling on first call.""" + user_id = uuid4() + model_id = uuid4() + + mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + side_effect=DependentModelOwnershipError( + dependent_model=MagicMock(), + dependent_id=model_id, + parent_model=User, + parent_id=user_id, + ), + ) + + db = AsyncMock(spec=AsyncSession) + mock_model = MagicMock() + mock_model.get_api_model_name.return_value.name_capital = "Model" + + with pytest.raises(UserOwnershipError): + await get_user_owned_object( + db=db, + model=mock_model, + model_id=model_id, + owner_id=user_id, + ) + + @pytest.mark.asyncio + async def test_same_user_and_model_ids_different_models(self, mocker): + """Verify function works correctly with multiple different model types.""" + user_id = uuid4() + model_id = uuid4() + + mock_get_nested = mocker.patch( + "app.api.common.utils.ownership.get_nested_model_by_id", + new_callable=AsyncMock, + return_value=MagicMock(), + ) + + db = AsyncMock(spec=AsyncSession) + + # Different model types + model_types = [ + MagicMock(name="ModelA"), + MagicMock(name="ModelB"), + MagicMock(name="ModelC"), + ] + + for model_type in model_types: + await get_user_owned_object( + db=db, + model=model_type, + model_id=model_id, + owner_id=user_id, + ) + + # Verify all calls were made with different models but same IDs + assert mock_get_nested.call_count == 3 + for i, call in enumerate(mock_get_nested.call_args_list): + assert call.kwargs["dependent_model"] == model_types[i] + assert call.kwargs["dependent_id"] == model_id + assert call.kwargs["parent_id"] == user_id diff --git a/backend/tests/unit/test_validation_patterns.py b/backend/tests/unit/test_validation_patterns.py new file mode 100644 index 0000000..cda4f54 --- /dev/null +++ b/backend/tests/unit/test_validation_patterns.py @@ -0,0 +1,385 @@ +"""Unit tests for schema validation patterns across the application. + +Tests comprehensive validation patterns for schemas using Pydantic. +Demonstrates how to test constraints, validators, and error cases. +""" + +from datetime import date +from decimal import Decimal + +import pytest +from pydantic import BaseModel, EmailStr, Field, ValidationError, field_validator + + +@pytest.mark.unit +class TestFieldValidators: + """Test various field validator patterns.""" + + def test_custom_field_validator_email(self): + """Test email validation.""" + + class ContactSchema(BaseModel): + email: str + + @field_validator("email") + @classmethod + def validate_email(cls, v: str) -> str: + if "@" not in v: + raise ValueError("Invalid email format") + return v.lower() + + # Valid email + schema = ContactSchema(email="Test@Example.COM") + assert schema.email == "test@example.com" + + # Invalid email + with pytest.raises(ValidationError) as exc_info: + ContactSchema(email="invalid") + errors = exc_info.value.errors() + assert any(e["loc"][0] == "email" for e in errors) + + def test_field_validator_with_dependencies(self): + """Test validator that depends on multiple fields.""" + + class DateRangeSchema(BaseModel): + start_date: date + end_date: date + + @field_validator("end_date") + @classmethod + def validate_date_range(cls, v: date, info) -> date: + start = info.data.get("start_date") + if start and v < start: + raise ValueError("end_date must be after start_date") + return v + + # Valid range + schema = DateRangeSchema( + start_date=date(2024, 1, 1), + end_date=date(2024, 12, 31), + ) + assert schema.start_date < schema.end_date + + # Invalid range + with pytest.raises(ValidationError) as exc_info: + DateRangeSchema( + start_date=date(2024, 12, 31), + end_date=date(2024, 1, 1), + ) + errors = exc_info.value.errors() + assert any(e["loc"][0] == "end_date" for e in errors) + + def test_field_validator_uppercase_conversion(self): + """Test validator that transforms data.""" + + class CodeSchema(BaseModel): + code: str = Field(min_length=3, max_length=10) + + @field_validator("code") + @classmethod + def normalize_code(cls, v: str) -> str: + return v.upper().strip() + + schema = CodeSchema(code=" abc ") + assert schema.code == "ABC" + assert len(schema.code) == 3 + + def test_multiple_validators_on_field(self): + """Test multiple validators on single field.""" + + class PercentageSchema(BaseModel): + percentage: float = Field(ge=0, le=100) + + @field_validator("percentage") + @classmethod + def round_percentage(cls, v: float) -> float: + return round(v, 2) + + # Valid with rounding + schema = PercentageSchema(percentage=75.123) + assert schema.percentage == 75.12 + + # Out of range + with pytest.raises(ValidationError): + PercentageSchema(percentage=150) + + +@pytest.mark.unit +class TestComplexFieldTypes: + """Test validation of complex field types.""" + + def test_decimal_field_validation(self): + """Test Decimal field validation.""" + + class PriceSchema(BaseModel): + price: Decimal = Field(decimal_places=2, max_digits=10) + + # Valid price + schema = PriceSchema(price="19.99") + assert isinstance(schema.price, Decimal) + assert schema.price == Decimal("19.99") + + # Too many decimal places + with pytest.raises(ValidationError): + PriceSchema(price="19.999") + + def test_list_field_validation(self): + """Test list field validation with constraints.""" + + class TagsSchema(BaseModel): + tags: list[str] = Field(min_length=1, max_length=5) + + # Valid list + schema = TagsSchema(tags=["python", "testing"]) + assert len(schema.tags) == 2 + + # Empty list + with pytest.raises(ValidationError): + TagsSchema(tags=[]) + + # Too many items + with pytest.raises(ValidationError): + TagsSchema(tags=["a", "b", "c", "d", "e", "f"]) + + def test_optional_field_validation(self): + """Test optional fields with None validation.""" + + class OptionalSchema(BaseModel): + required_field: str + optional_field: str | None = None + optional_with_default: int | None = 42 + + # With optional fields + schema = OptionalSchema(required_field="test") + assert schema.optional_field is None + assert schema.optional_with_default == 42 + + # With optional fields provided + schema2 = OptionalSchema( + required_field="test", + optional_field="provided", + optional_with_default=100, + ) + assert schema2.optional_field == "provided" + assert schema2.optional_with_default == 100 + + def test_nested_model_validation(self): + """Test validation of nested Pydantic models.""" + + class AddressSchema(BaseModel): + street: str + city: str + country: str = "US" + + class PersonSchema(BaseModel): + name: str + address: AddressSchema + + # Valid nested + schema = PersonSchema( + name="John", + address={"street": "123 Main St", "city": "Boston"}, + ) + assert schema.address.city == "Boston" + assert schema.address.country == "US" + + # Invalid nested + with pytest.raises(ValidationError) as exc_info: + PersonSchema( + name="John", + address={"street": "123 Main St"}, # Missing city + ) + errors = exc_info.value.errors() + assert any("address" in str(e["loc"]) for e in errors) + + def test_list_of_nested_models(self): + """Test validation of lists of nested models.""" + + class ItemSchema(BaseModel): + id: int + name: str + + class OrderSchema(BaseModel): + items: list[ItemSchema] + + # Valid list of nested models + schema = OrderSchema( + items=[ + {"id": 1, "name": "Item 1"}, + {"id": 2, "name": "Item 2"}, + ] + ) + assert len(schema.items) == 2 + assert schema.items[0].name == "Item 1" + + # Invalid nested item + with pytest.raises(ValidationError): + OrderSchema( + items=[ + {"id": 1, "name": "Item 1"}, + {"id": 2}, # Missing name + ] + ) + + +@pytest.mark.unit +class TestErrorHandling: + """Test error handling and validation error details.""" + + def test_validation_error_contains_field_info(self): + """Test that ValidationError contains field information.""" + + class StrictSchema(BaseModel): + email: EmailStr + age: int = Field(ge=0, le=150) + + with pytest.raises(ValidationError) as exc_info: + StrictSchema(email="invalid", age=200) + + errors = exc_info.value.errors() + assert len(errors) == 2 + + # Check that field names are in errors + error_fields = {e["loc"][0] for e in errors} + assert "email" in error_fields + assert "age" in error_fields + + def test_validation_error_messages(self): + """Test that error messages are helpful.""" + + class MessageSchema(BaseModel): + text: str = Field(min_length=5, max_length=100) + + with pytest.raises(ValidationError) as exc_info: + MessageSchema(text="hi") + + errors = exc_info.value.errors() + error_messages = [e["msg"] for e in errors] + # Should contain length constraint info + assert any("String should have at least 5 characters" in str(msg) for msg in error_messages) + + def test_multiple_validation_errors_collected(self): + """Test that all validation errors are collected, not just first.""" + + class MultiSchema(BaseModel): + name: str = Field(min_length=1) + age: int = Field(ge=0, le=150) + email: EmailStr + + # Multiple errors should all be reported + with pytest.raises(ValidationError) as exc_info: + MultiSchema(name="", age=999, email="invalid") + + errors = exc_info.value.errors() + error_fields = {e["loc"][0] for e in errors} + assert "name" in error_fields + assert "age" in error_fields + assert "email" in error_fields + + +@pytest.mark.unit +class TestEnumValidation: + """Test validation of enum fields.""" + + def test_enum_string_validation(self): + """Test string enum validation.""" + from enum import Enum + + class StatusEnum(str, Enum): + ACTIVE = "active" + INACTIVE = "inactive" + PENDING = "pending" + + class StatusSchema(BaseModel): + status: StatusEnum + + # Valid enum value + schema = StatusSchema(status="active") + assert schema.status == StatusEnum.ACTIVE + + # Invalid enum value + with pytest.raises(ValidationError): + StatusSchema(status="invalid") + + def test_enum_int_validation(self): + """Test integer enum validation.""" + from enum import Enum + + class LevelEnum(int, Enum): + LOW = 1 + MEDIUM = 2 + HIGH = 3 + + class LevelSchema(BaseModel): + level: LevelEnum + + # Valid enum value + schema = LevelSchema(level=2) + assert schema.level == LevelEnum.MEDIUM + + # Invalid enum value + with pytest.raises(ValidationError): + LevelSchema(level=99) + + +@pytest.mark.unit +class TestConditionalValidation: + """Test conditional validation logic.""" + + def test_required_if_another_field_present(self): + """Test field is required only if another field is present.""" + + class ConditionalSchema(BaseModel): + has_discount: bool = False + discount_code: str | None = None + + @field_validator("discount_code") + @classmethod + def validate_discount_code(cls, v: str | None, info) -> str | None: + has_discount = info.data.get("has_discount") + if has_discount and not v: + raise ValueError("discount_code required when has_discount is True") + return v + + # Valid: no discount, no code needed + schema = ConditionalSchema(has_discount=False) + assert schema.discount_code is None + + # Valid: has discount with code + schema2 = ConditionalSchema(has_discount=True, discount_code="SAVE10") + assert schema2.discount_code == "SAVE10" + + # Invalid: has discount but no code + with pytest.raises(ValidationError): + ConditionalSchema(has_discount=True, discount_code=None) + + def test_mutually_exclusive_fields(self): + """Test mutually exclusive fields validation.""" + + class MutualSchema(BaseModel): + payment_method: str + credit_card: str | None = None + bank_account: str | None = None + + @field_validator("bank_account") + @classmethod + def validate_mutually_exclusive(cls, v: str | None, info) -> str | None: + credit_card = info.data.get("credit_card") + if v and credit_card: + raise ValueError("Cannot specify both credit_card and bank_account") + return v + + # Valid: only credit card + schema = MutualSchema( + payment_method="card", + credit_card="1234", + ) + assert schema.credit_card == "1234" + + # Invalid: both specified + with pytest.raises(ValidationError): + MutualSchema( + payment_method="mixed", + credit_card="1234", + bank_account="5678", + ) From cb8c25916fde65c199a130b6adba8cd3cc02aff1 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 17 Feb 2026 15:43:33 +0100 Subject: [PATCH 78/79] fix(backend): some type fixes in scripts --- backend/scripts/seed/dummy_data.py | 67 +++++++++++-------- backend/scripts/seed/taxonomies/common.py | 6 +- backend/scripts/seed/taxonomies/cpv.py | 11 ++- .../seed/taxonomies/harmonized_system.py | 11 ++- 4 files changed, 62 insertions(+), 33 deletions(-) diff --git a/backend/scripts/seed/dummy_data.py b/backend/scripts/seed/dummy_data.py index a6c2792..a4a5de1 100755 --- a/backend/scripts/seed/dummy_data.py +++ b/backend/scripts/seed/dummy_data.py @@ -4,15 +4,15 @@ import asyncio import contextlib +import io import logging import mimetypes from typing import TYPE_CHECKING +import anyio from fastapi import UploadFile -from sqlmodel.ext.asyncio.session import AsyncSession from starlette.datastructures import Headers -from app.api.auth.models import User from app.api.auth.schemas import UserCreate from app.api.auth.utils.programmatic_user_crud import create_user from app.api.background_data.models import ( @@ -38,12 +38,17 @@ if TYPE_CHECKING: from pathlib import Path + from sqlmodel.ext.asyncio.session import AsyncSession + + from app.api.auth.models import User + # Set up logging logger: logging.Logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) ### Sample Data ### # TODO: Add organization and Camera models + # Sample data for Users user_data = [ { @@ -174,10 +179,10 @@ ] # Sample data for Images -image_data = [ +image_data: list[dict[str, str]] = [ { "description": "Example phone image", - "path": settings.static_files_path / "images" / "example_phone.jpg", + "path": str(settings.static_files_path / "images" / "example_phone.jpg"), "parent_product_name": "iPhone 12", } ] @@ -326,8 +331,8 @@ async def seed_products( async def seed_images(session: AsyncSession, product_map: dict[str, Product]) -> None: """Seed the database with initial image data.""" for data in image_data: - path: Path = data["path"] - description: str = data["description"] + path: Path = Path(data.get("path", None)) + description: str = data.get("description", "") parent_type = ImageParentType.PRODUCT parent = product_map.get(data["parent_product_name"]) @@ -338,34 +343,40 @@ async def seed_images(session: AsyncSession, product_map: dict[str, Product]) -> continue filename: str = path.name - size: int = path.stat().st_size + async_path = anyio.Path(path) + size: int = (await async_path.stat()).st_size mime_type, _ = mimetypes.guess_type(path) if mime_type is None: err_msg = f"Could not determine MIME type for image file {filename}." raise ValueError(err_msg) - with path.open("rb") as file: - upload_file = UploadFile( - file=file, - filename=filename, - size=size, - headers=Headers( - { - "filename": filename, - "size": str(size), - "content-type": mime_type, - } - ), - ) - - image_create = ImageCreateFromForm( - description=description, - file=upload_file, - parent_id=parent_id, - parent_type=parent_type, - ) - await create_image(session, image_create) + # Read file into memory + async with await async_path.open("rb") as file: + file_content = await file.read() + + # Create BytesIO object for UploadFile + file_obj = io.BytesIO(file_content) + upload_file = UploadFile( + file=file_obj, + filename=filename, + size=size, + headers=Headers( + { + "filename": filename, + "size": str(size), + "content-type": mime_type, + } + ), + ) + + image_create = ImageCreateFromForm( + description=description, + file=upload_file, + parent_id=parent_id, + parent_type=parent_type, + ) + await create_image(session, image_create) async def async_main() -> None: diff --git a/backend/scripts/seed/taxonomies/common.py b/backend/scripts/seed/taxonomies/common.py index aadbf16..795c4a6 100644 --- a/backend/scripts/seed/taxonomies/common.py +++ b/backend/scripts/seed/taxonomies/common.py @@ -58,7 +58,7 @@ def get_or_create_taxonomy( def seed_categories_from_rows( session: Session, - taxonomy: Taxonomy, + taxonomy_id: int, rows: list[dict[str, Any]], get_parent_id_fn: Callable[[dict[str, Any]], str | None], ) -> tuple[int, int]: @@ -66,7 +66,7 @@ def seed_categories_from_rows( Args: session: Database session - taxonomy: The taxonomy to add categories to (must be committed with non-None ID) + taxonomy_id: The taxonomy ID to add categories to (must be committed with non-None ID) rows: List of dictionaries with category data (must have 'external_id' and 'name') get_parent_id_fn: Function that takes a row and returns parent external_id or None @@ -86,7 +86,7 @@ def seed_categories_from_rows( category = Category( name=name, external_id=external_id, - taxonomy_id=taxonomy.id, + taxonomy_id=taxonomy_id, ) session.add(category) id_to_category[external_id] = category diff --git a/backend/scripts/seed/taxonomies/cpv.py b/backend/scripts/seed/taxonomies/cpv.py index 5afc655..16cccfc 100644 --- a/backend/scripts/seed/taxonomies/cpv.py +++ b/backend/scripts/seed/taxonomies/cpv.py @@ -164,6 +164,15 @@ def seed_taxonomy(excel_path: Path = EXCEL_PATH) -> None: source=TAXONOMY_SOURCE, ) + if taxonomy.id is None: + # TODO: Refactor base models so that comitted database objects always have non-None ID to avoid this check + logger.error( + "Taxonomy '%s' version '%s' has no ID after creation, cannot seed categories.", + TAXONOMY_NAME, + TAXONOMY_VERSION, + ) + return + # If taxonomy already existed, skip seeding existing_count = session.exec(select(func.count(Category.id)).where(Category.taxonomy_id == taxonomy.id)).one() @@ -176,7 +185,7 @@ def seed_taxonomy(excel_path: Path = EXCEL_PATH) -> None: logger.info("Loaded %d CPV codes from Excel", len(rows)) # Seed categories - cat_count, rel_count = seed_categories_from_rows(session, taxonomy, rows, get_parent_id_fn=get_cpv_parent_id) + cat_count, rel_count = seed_categories_from_rows(session, taxonomy.id, rows, get_parent_id_fn=get_cpv_parent_id) # Commit # session.commit() diff --git a/backend/scripts/seed/taxonomies/harmonized_system.py b/backend/scripts/seed/taxonomies/harmonized_system.py index e860b6b..95d273a 100644 --- a/backend/scripts/seed/taxonomies/harmonized_system.py +++ b/backend/scripts/seed/taxonomies/harmonized_system.py @@ -97,6 +97,15 @@ def seed_taxonomy() -> None: source=TAXONOMY_SOURCE, ) + if taxonomy.id is None: + # TODO: Refactor base models so that comitted database objects always have non-None ID to avoid this check + logger.error( + "Taxonomy '%s' version '%s' has no ID after creation, cannot seed categories.", + TAXONOMY_NAME, + TAXONOMY_VERSION, + ) + return + # If taxonomy already existed, skip seeding existing_count = session.exec(select(func.count(Category.id)).where(Category.taxonomy_id == taxonomy.id)).one() @@ -108,7 +117,7 @@ def seed_taxonomy() -> None: rows = load_hs_rows_from_csv(CSV_PATH) # Seed categories - cat_count, rel_count = seed_categories_from_rows(session, taxonomy, rows, get_parent_id_fn=get_hs_parent_id) + cat_count, rel_count = seed_categories_from_rows(session, taxonomy.id, rows, get_parent_id_fn=get_hs_parent_id) # Commit session.commit() From 7b2637495de1386066d5ded75ae8e19ac014e227 Mon Sep 17 00:00:00 2001 From: Simon van Lierde Date: Tue, 17 Feb 2026 15:49:18 +0100 Subject: [PATCH 79/79] fix(backend): Some type and linting fixes --- .../app/api/auth/utils/programmatic_emails.py | 25 ++++++------ backend/app/api/background_data/schemas.py | 40 +++++++++---------- backend/app/api/common/models/custom_types.py | 1 + .../api/file_storage/models/custom_types.py | 6 ++- backend/app/api/plugins/rpi_cam/services.py | 4 +- backend/app/main.py | 2 +- 6 files changed, 38 insertions(+), 40 deletions(-) diff --git a/backend/app/api/auth/utils/programmatic_emails.py b/backend/app/api/auth/utils/programmatic_emails.py index 6c02f37..49b67f0 100644 --- a/backend/app/api/auth/utils/programmatic_emails.py +++ b/backend/app/api/auth/utils/programmatic_emails.py @@ -1,16 +1,18 @@ """Utilities for sending authentication-related emails using fastapi-mail.""" import logging -from typing import Any +from typing import TYPE_CHECKING, Any from urllib.parse import urljoin -from fastapi import BackgroundTasks from fastapi_mail import MessageSchema, MessageType -from pydantic import AnyUrl, EmailStr +from pydantic import AnyUrl, EmailStr, NameEmail from app.api.auth.utils.email_config import fm from app.core.config import settings as core_settings +if TYPE_CHECKING: + from fastapi import BackgroundTasks + logger: logging.Logger = logging.getLogger(__name__) @@ -23,7 +25,7 @@ def generate_token_link(token: str, route: str, base_url: str | AnyUrl | None = return urljoin(str(base_url), f"{route}?token={token}") -def mask_email_for_log(email: EmailStr, mask: bool = True, max_len: int = 80) -> str: +def mask_email_for_log(email: EmailStr, *, mask: bool = True, max_len: int = 80) -> str: """Mask emails for logging. Also remove non-printable characters and truncates long domains. Explicitly removes log-breaking control characters. @@ -31,10 +33,7 @@ def mask_email_for_log(email: EmailStr, mask: bool = True, max_len: int = 80) -> # Remove non-printable and log-breaking control characters string = "".join(ch for ch in str(email) if ch.isprintable()).replace("\n", "").replace("\r", "") local, sep, domain = string.partition("@") - if sep and mask: - masked = f"{local[0]}***@{domain}" if len(local) > 1 else f"*@{domain}" - else: - masked = string + masked = (f"{local[0]}***@{domain}" if len(local) > 1 else f"*@{domain}") if sep and mask else string return f"{masked[: max_len - 3]}..." if len(masked) > max_len else masked @@ -57,7 +56,7 @@ async def send_email_with_template( """ message = MessageSchema( subject=subject, - recipients=[to_email], + recipients=[NameEmail(name=str(to_email), email=str(to_email))], template_body=template_body, subtype=MessageType.html, ) @@ -88,7 +87,7 @@ async def send_registration_email( subject=subject, template_name="registration.html", template_body={ - "username": username if username else to_email, + "username": username or to_email, "verification_link": verification_link, }, background_tasks=background_tasks, @@ -110,7 +109,7 @@ async def send_reset_password_email( subject=subject, template_name="password_reset.html", template_body={ - "username": username if username else to_email, + "username": username or to_email, "reset_link": reset_link, }, background_tasks=background_tasks, @@ -132,7 +131,7 @@ async def send_verification_email( subject=subject, template_name="verification.html", template_body={ - "username": username if username else to_email, + "username": username or to_email, "verification_link": verification_link, }, background_tasks=background_tasks, @@ -152,7 +151,7 @@ async def send_post_verification_email( subject=subject, template_name="post_verification.html", template_body={ - "username": username if username else to_email, + "username": username or to_email, }, background_tasks=background_tasks, ) diff --git a/backend/app/api/background_data/schemas.py b/backend/app/api/background_data/schemas.py index 91c7f6b..e04f3e7 100644 --- a/backend/app/api/background_data/schemas.py +++ b/backend/app/api/background_data/schemas.py @@ -13,6 +13,7 @@ from app.api.common.schemas.base import ( BaseCreateSchema, BaseReadSchema, + BaseReadSchemaWithTimeStamp, BaseUpdateSchema, MaterialRead, ProductRead, @@ -33,8 +34,8 @@ class CategoryCreateWithinCategoryWithSubCategories(BaseCreateSchema, CategoryBa """Schema for creating a new category within a category, with optional subcategories.""" # Database model has a None default, but Pydantic model has empty set default for consistent API type handling - subcategories: set[CategoryCreateWithinCategoryWithSubCategories] = Field( - default_factory=set, + subcategories: list[CategoryCreateWithinCategoryWithSubCategories] = Field( + default_factory=list, description="List of subcategories", ) @@ -191,13 +192,13 @@ class TaxonomyCreate(BaseCreateSchema, TaxonomyBase): class TaxonomyCreateWithCategories(BaseCreateSchema, TaxonomyBase): """Schema for creating a new taxonomy, optionally with new categories.""" - categories: set[CategoryCreateWithinTaxonomyWithSubCategories] = Field( - default_factory=set, description="Set of subcategories" + categories: list[CategoryCreateWithinTaxonomyWithSubCategories] = Field( + default_factory=list, description="Set of subcategories" ) ## Read Schemas ## -class TaxonomyRead(BaseReadSchema, TaxonomyBase): +class TaxonomyRead(BaseReadSchemaWithTimeStamp, TaxonomyBase): """Schema for reading minimal taxonomy information.""" model_config: ConfigDict = ConfigDict( @@ -260,7 +261,8 @@ class TaxonomyUpdate(BaseUpdateSchema): version: str | None = Field(default=None, min_length=1, max_length=50) description: str | None = Field(default=None, max_length=500) domains: set[TaxonomyDomain] | None = Field( - description="Domains of the taxonomy, e.g. {" + f"{', '.join([d.value for d in TaxonomyDomain][:3])}" + "}" + default=None, + description="Domains of the taxonomy, e.g. {" + f"{', '.join([d.value for d in TaxonomyDomain][:3])}" + "}", ) source: str | None = Field(default=None, max_length=50, description="Source of the taxonomy data") @@ -299,12 +301,10 @@ class MaterialReadWithRelationships(MaterialRead): class MaterialUpdate(BaseUpdateSchema): """Schema for a partial update of a material.""" - name: str | None = Field(default=None, min_length=2, max_length=100, description="Name of the Material") - description: str | None = Field(default=None, max_length=500, description="Description of the Material") + name: str | None = Field(default=None, min_length=2, max_length=100) + description: str | None = Field(default=None, max_length=500) source: str | None = Field( - default=None, - max_length=50, - description="Source of the material data, e.g. URL, IRI or citation key", + default=None, max_length=50, description="Source of the material data, e.g. URL, IRI or citation key" ) density_kg_m3: float | None = Field(default=None, gt=0, description="Volumetric density (kg/m³) ") is_crm: bool | None = Field(default=None, description="Is this material a Critical Raw Material (CRM)?") @@ -319,7 +319,7 @@ class ProductTypeCreate(BaseCreateSchema, ProductTypeBase): class ProductTypeCreateWithCategories(BaseCreateSchema, ProductTypeBase): """Schema for creating a product type with links to existing categories.""" - category_ids: set[int] = Field(default_factory=set, description="List of category IDs") + category_ids: set[int] = Field(default_factory=set) ## Read Schemas ## @@ -330,19 +330,15 @@ class ProductTypeRead(BaseReadSchema, ProductTypeBase): class ProductTypeReadWithRelationships(ProductTypeRead): """Schema for reading product type information with all relationships.""" - products: list[ProductRead] = Field( - default_factory=list, description="List of products that have this product type" - ) - categories: list[CategoryRead] = Field( - default_factory=list, description="List of categories linked to the product type" - ) - images: list[ImageRead] = Field(default_factory=list, description="List of images for the product type") - files: list[FileRead] = Field(default_factory=list, description="List of files for the product type") + products: list[ProductRead] = Field(default_factory=list) + categories: list[CategoryRead] = Field(default_factory=list) + images: list[ImageRead] = Field(default_factory=list) + files: list[FileRead] = Field(default_factory=list) ## Update Schemas ## class ProductTypeUpdate(BaseUpdateSchema): """Schema for a partial update of a product type.""" - name: str | None = Field(default=None, min_length=2, max_length=100, description="Name of the Product Type.") - description: str | None = Field(default=None, max_length=500, description="Description of the Product Type.") + name: str | None = Field(default=None, min_length=2, max_length=100) + description: str | None = Field(default=None, max_length=500) diff --git a/backend/app/api/common/models/custom_types.py b/backend/app/api/common/models/custom_types.py index 37a5575..e47467b 100644 --- a/backend/app/api/common/models/custom_types.py +++ b/backend/app/api/common/models/custom_types.py @@ -11,6 +11,7 @@ ### Type aliases ### # Type alias for ID types IDT = TypeVar("IDT", bound=int | UUID) + ### TypeVars ### # TypeVar for models MT = TypeVar("MT", bound=CustomBaseBare) diff --git a/backend/app/api/file_storage/models/custom_types.py b/backend/app/api/file_storage/models/custom_types.py index 602e7e2..40d2184 100644 --- a/backend/app/api/file_storage/models/custom_types.py +++ b/backend/app/api/file_storage/models/custom_types.py @@ -1,15 +1,17 @@ """Custom types for FastAPI Storages models.""" -from typing import Any, BinaryIO +from typing import TYPE_CHECKING, Any, BinaryIO from fastapi_storages import FileSystemStorage, StorageImage from fastapi_storages.integrations.sqlalchemy import FileType as _FileType from fastapi_storages.integrations.sqlalchemy import ImageType as _ImageType -from sqlalchemy import Dialect from app.api.file_storage.exceptions import FastAPIStorageFileNotFoundError from app.core.config import settings +if TYPE_CHECKING: + from sqlalchemy import Dialect + ## Custom error handling for file not found in storage class CustomFileSystemStorage(FileSystemStorage): diff --git a/backend/app/api/plugins/rpi_cam/services.py b/backend/app/api/plugins/rpi_cam/services.py index ca56db4..8040449 100644 --- a/backend/app/api/plugins/rpi_cam/services.py +++ b/backend/app/api/plugins/rpi_cam/services.py @@ -63,11 +63,11 @@ async def capture_and_store_image( image_data = ImageCreateInternal( file=UploadFile( file=BytesIO(image_response.content), - filename=filename if filename else f"{camera.name}_{serialize_datetime_with_z(datetime.now(UTC))}.jpg", + filename=filename or f"{camera.name}_{serialize_datetime_with_z(datetime.now(UTC))}.jpg", size=len(image_response.content), headers=Headers({"content-type": "image/jpeg"}), ), - description=(description if description else f"Captured from camera {camera.name} at {timestamp_str}."), + description=(description or f"Captured from camera {camera.name} at {timestamp_str}."), image_metadata=capture_data.get("metadata"), parent_type=ImageParentType.PRODUCT, parent_id=product_id, diff --git a/backend/app/main.py b/backend/app/main.py index c42a6b9..8357c7b 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -82,7 +82,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator: # Add CORS middleware app.add_middleware( - CORSMiddleware, + CORSMiddleware, # ty: ignore[invalid-argument-type] # Known false positive https://github.com/astral-sh/ty/issues/1635 allow_origins=settings.allowed_origins, allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],