From 1e1000268ae81f3d08e6a266e85d7f272466c8b0 Mon Sep 17 00:00:00 2001 From: dreamer Date: Tue, 13 Oct 2026 16:10:30 +0200 Subject: [PATCH] getting started with backend and uploader --- .gitignore | 4 + README.md | 21 + docs/adr/ADR-001-initial-architecture.md | 45 + fdhview/__init__.py | 0 fdhview/api/__init__.py | 0 fdhview/api/admin.py | 52 + fdhview/api/deps.py | 25 + fdhview/api/screenings.py | 42 + fdhview/core/__init__.py | 0 fdhview/core/config.py | 29 + fdhview/core/database.py | 14 + fdhview/html/upload.html | 43 + fdhview/main.py | 26 + fdhview/models/__init__.py | 0 fdhview/models/enums.py | 28 + fdhview/models/screening.py | 35 + fdhview/models/tags.py | 60 ++ fdhview/schemas/__init__.py | 0 fdhview/schemas/screening.py | 21 + fdhview/services/__init__.py | 0 fdhview/services/screenwriter/__init__.py | 0 fdhview/services/screenwriter/importer.py | 96 ++ fdhview/services/screenwriter/parser.py | 17 + fdhview/services/screenwriter/schema.py | 34 + fdhview/static/12.png | Bin 0 -> 3189 bytes fdhview/static/14.png | Bin 0 -> 2853 bytes fdhview/static/16.png | Bin 0 -> 2840 bytes fdhview/static/18.png | Bin 0 -> 2876 bytes fdhview/static/6.png | Bin 0 -> 2660 bytes fdhview/static/9.png | Bin 0 -> 2660 bytes fdhview/static/Logo.svg | 19 + fdhview/static/al.png | Bin 0 -> 2635 bytes fdhview/static/angst.png | Bin 0 -> 4322 bytes fdhview/static/discriminatie.png | Bin 0 -> 3771 bytes fdhview/static/drugs-en-alcohol.png | Bin 0 -> 3189 bytes fdhview/static/gevaarlijk-gedrag.png | Bin 0 -> 3982 bytes fdhview/static/geweld.png | Bin 0 -> 4066 bytes fdhview/static/seks.png | Bin 0 -> 4063 bytes fdhview/static/style.css | 69 ++ fdhview/static/taal.png | Bin 0 -> 3549 bytes poetry.lock | 1181 +++++++++++++++++++++ pyproject.toml | 36 + scripts/inspect_xml.py | 30 + tests/__init__.py | 0 tests/conftest.py | 52 + tests/data/broken.xml | 59 + tests/data/minimal.xml | 59 + tests/test_admin_api.py | 38 + tests/test_importer.py | 49 + tests/test_screenings_api.py | 53 + 50 files changed, 2237 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 docs/adr/ADR-001-initial-architecture.md create mode 100644 fdhview/__init__.py create mode 100644 fdhview/api/__init__.py create mode 100644 fdhview/api/admin.py create mode 100644 fdhview/api/deps.py create mode 100644 fdhview/api/screenings.py create mode 100644 fdhview/core/__init__.py create mode 100644 fdhview/core/config.py create mode 100644 fdhview/core/database.py create mode 100644 fdhview/html/upload.html create mode 100644 fdhview/main.py create mode 100644 fdhview/models/__init__.py create mode 100644 fdhview/models/enums.py create mode 100644 fdhview/models/screening.py create mode 100644 fdhview/models/tags.py create mode 100644 fdhview/schemas/__init__.py create mode 100644 fdhview/schemas/screening.py create mode 100644 fdhview/services/__init__.py create mode 100644 fdhview/services/screenwriter/__init__.py create mode 100644 fdhview/services/screenwriter/importer.py create mode 100644 fdhview/services/screenwriter/parser.py create mode 100644 fdhview/services/screenwriter/schema.py create mode 100644 fdhview/static/12.png create mode 100644 fdhview/static/14.png create mode 100644 fdhview/static/16.png create mode 100644 fdhview/static/18.png create mode 100644 fdhview/static/6.png create mode 100644 fdhview/static/9.png create mode 100644 fdhview/static/Logo.svg create mode 100644 fdhview/static/al.png create mode 100644 fdhview/static/angst.png create mode 100644 fdhview/static/discriminatie.png create mode 100644 fdhview/static/drugs-en-alcohol.png create mode 100644 fdhview/static/gevaarlijk-gedrag.png create mode 100644 fdhview/static/geweld.png create mode 100644 fdhview/static/seks.png create mode 100644 fdhview/static/style.css create mode 100644 fdhview/static/taal.png create mode 100644 poetry.lock create mode 100644 pyproject.toml create mode 100644 scripts/inspect_xml.py create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/data/broken.xml create mode 100644 tests/data/minimal.xml create mode 100644 tests/test_admin_api.py create mode 100644 tests/test_importer.py create mode 100644 tests/test_screenings_api.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..92d0c06 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.db +*.venv/ +*.env +*cache* diff --git a/README.md b/README.md new file mode 100644 index 0000000..b674783 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# FDH View + +## Description + +Filmhuis Den Haag schedule viewer using FastAPI and SQLModel. + +Ingests a ScreenWriter XML file with theater screening schedule. + +Backend has an API with a main ticketing booth overview and theater hall specific endpoints. + +The endpoint serves the 10 upcoming screenings for the day. The frontend automatically refreshes to update its view with the current information. + +## Run + +`poetry run uvicorn fdhview.main:app --reload` + +## Endpoints + +* GET `/admin` - upload form protected with Basic Auth. +* POST `/admin/upload` - upload endpoint protected with Basic Auth +* GET `/api/screenings` - serves JSON with upcoming 10 films for today diff --git a/docs/adr/ADR-001-initial-architecture.md b/docs/adr/ADR-001-initial-architecture.md new file mode 100644 index 0000000..dbf2e79 --- /dev/null +++ b/docs/adr/ADR-001-initial-architecture.md @@ -0,0 +1,45 @@ +# ADR-001 Initial Architecture + +Date: 2026-10-13 + +## Context + +Filmhuis Den Haag (FDH) has a display above the ticket counter that shows ticket pricing information and upcoming screenings with ticket sales. Currently it's running a proprietary system that is leased from a third party. Since FDH already has all feature and ticket information available, as part of a weekly ScreenWriter XML, the desire is to have their own solution to replace the proprietary system. + +It needs to display both ticket pricing info and upcoming features with information like the start and end times, KijkWijzer ratings, theater room, and other useful data. + +## Decision + +A FastAPI backend will be able to ingest the ScreenWriter XML and collect all feature information into a simple database. Subsequently for ticketing info a JSON endpoint will serve the next 10 upcoming features. A simple frontend page can automatically refresh every couple minutes in order to display the latest valid information. + +When a new XML is uploaded it replaces all information in the database with the new data. This way the backend only holds currently relevant information. + +The upload endpoints will be secured using a basic auth password. + +For each screening at least the following information is stored: + +* unique session id - int, as reference +* screen - which screen/room is it being displayed +* start time - timestamp +* end time - timestamp +* duration - in seconds +* title - str +* rating - int +* seats_total - int, called seats_avaialble in XML +* seats_sold - int +* en_subs - bool, if the feature has english subtitles +* intro - bool, if there is an introductory talk +* outro - bool, if there is an after talk +* kijkwijzer - list of kijkwijzer ids +* tags - any additional tags that may be useful + +## MVP Definition + +An authenticated user needs to be able to log into the upload page and provide an XML with data. Any (parsing) errors will be displayed to the user if there are issues. + +A simple browser client connected to a display can then open a html page that will automatically refresh every couple minutes with updated/current information. + +## Future Improvements + +* add display endpoints for each of the theater rooms with current and upcoming feature information +* be able to synchronize with a ticketing backend without the user having to upload an XML diff --git a/fdhview/__init__.py b/fdhview/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fdhview/api/__init__.py b/fdhview/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fdhview/api/admin.py b/fdhview/api/admin.py new file mode 100644 index 0000000..de3b330 --- /dev/null +++ b/fdhview/api/admin.py @@ -0,0 +1,52 @@ +from pathlib import Path + +from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status +from fastapi.responses import FileResponse, JSONResponse +from sqlmodel.ext.asyncio.session import AsyncSession + +from fdhview.api.deps import check_auth, get_session +from fdhview.services.screenwriter.importer import import_sessions +from fdhview.services.screenwriter.parser import XmlParseError, parse_screenwriter_xml + +BASE_DIR = Path(__file__).resolve().parent.parent + +router = APIRouter( + prefix="/admin", + tags=["Admin"], + dependencies=[Depends(check_auth)] +) + +@router.get("", response_class=FileResponse) +async def upload_form(): + return FileResponse(BASE_DIR / "html" / "upload.html") + + +@router.post("/upload", response_class=JSONResponse) +async def upload_file( + file: UploadFile = File(...), + session: AsyncSession = Depends(get_session) +): + if not file.filename or not file.filename.lower().endswith(".xml"): + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, + detail="File must be an XML file" + ) + + contents = await file.read() + if len(contents) > 10 * 1024 * 1024: + raise HTTPException( + status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, + detail="File too large" + ) + + try: + aampos = parse_screenwriter_xml(contents) + except XmlParseError as e: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, + detail=str(e) + ) + + await import_sessions(aampos, session=session) + + return JSONResponse(content={"filename": file.filename, "size": len(contents)}) diff --git a/fdhview/api/deps.py b/fdhview/api/deps.py new file mode 100644 index 0000000..3d90e4d --- /dev/null +++ b/fdhview/api/deps.py @@ -0,0 +1,25 @@ +import secrets +from collections.abc import AsyncGenerator + +from fastapi import Depends, HTTPException, status +from fastapi.security import HTTPBasic, HTTPBasicCredentials +from sqlmodel.ext.asyncio.session import AsyncSession + +from fdhview.core.config import settings +from fdhview.core.database import async_session + +security = HTTPBasic() + + +async def get_session() -> AsyncGenerator[AsyncSession]: + async with async_session() as session: + yield session + + +def check_auth(credentials: HTTPBasicCredentials = Depends(security)): + if not secrets.compare_digest(credentials.password.encode(), settings.api_password.encode()): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid credentials", + headers={"WWW-Authenticate": "Basic"}, + ) diff --git a/fdhview/api/screenings.py b/fdhview/api/screenings.py new file mode 100644 index 0000000..5c61cc5 --- /dev/null +++ b/fdhview/api/screenings.py @@ -0,0 +1,42 @@ +from datetime import datetime +from zoneinfo import ZoneInfo + +from fastapi import APIRouter, Depends +from sqlmodel import col, select +from sqlmodel.ext.asyncio.session import AsyncSession + +from fdhview.api.deps import get_session +from fdhview.core.config import settings +from fdhview.models.screening import Screening +from fdhview.schemas.screening import ScreeningRead + +router = APIRouter(prefix="/screenings", tags=["Screenings"]) + + +@router.get( + "", + response_model=list[ScreeningRead], + summary="Get upcoming screenings for the infodesk" +) +async def get_infodesk_screenings( + session: AsyncSession = Depends(get_session) +) -> list[ScreeningRead]: + now = datetime.now(tz=ZoneInfo(settings.timezone)) + + query = ( + select(Screening) + .where(Screening.start > now) + .order_by(col(Screening.start)) + .limit(10) + ) + screenings = await session.exec(query) + return [ScreeningRead.model_validate(s) for s in screenings.all()] + + +@router.get( + "/prices", + response_model=dict, + summary="Get pricing information" +) +async def get_screening_prices() -> dict: + return settings.prices diff --git a/fdhview/core/__init__.py b/fdhview/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fdhview/core/config.py b/fdhview/core/config.py new file mode 100644 index 0000000..f0fec47 --- /dev/null +++ b/fdhview/core/config.py @@ -0,0 +1,29 @@ +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class Settings(BaseSettings): + model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8") + debug: bool = False + api_password: str = "testertje" + sql_file_name: str = "database.db" + timezone: str = "Europe/Amsterdam" + prices: dict = { + "prijzen": [ + {"label": "Standaard", "price": "€ 12,50"}, + {"label": "Dagtarief (ma–vr tot 16u)", "price": "€ 11,00"}, + {"label": "Cineville", "price": "Gratis"}, + {"label": "Lange film (>180 min)", "price": "€ 15,00"}, + {"label": "70MM film", "price": "€ 15,00"}, + ], + "kortingsprijzen": [ + {"label": "Jongeren (t/m 26 jaar)", "price": "€ 10,00"}, + {"label": "Student Nights", "price": "€ 5,00"}, + {"label": "CJP", "price": "€ 10,00"}, + {"label": "Vrienden", "price": "€ 9,50"}, + {"label": "Kinderfilm (t/m 11 jaar)", "price": "€ 8,00"}, + {"label": "Ooievaarspas", "price": "€ 6,25"}, + {"label": "De Volharding", "price": "€ 9,00"}, + ], + } + +settings = Settings() diff --git a/fdhview/core/database.py b/fdhview/core/database.py new file mode 100644 index 0000000..ccc5794 --- /dev/null +++ b/fdhview/core/database.py @@ -0,0 +1,14 @@ +from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine +from sqlmodel import SQLModel +from sqlmodel.ext.asyncio.session import AsyncSession + +from fdhview.core.config import settings + +sqlite_url = f"sqlite+aiosqlite:///{settings.sql_file_name}" + +engine = create_async_engine(sqlite_url, echo=settings.debug) +async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) + +async def init_db(): + async with engine.begin() as conn: + await conn.run_sync(SQLModel.metadata.create_all) diff --git a/fdhview/html/upload.html b/fdhview/html/upload.html new file mode 100644 index 0000000..a8bbbc6 --- /dev/null +++ b/fdhview/html/upload.html @@ -0,0 +1,43 @@ + + + Upload Schedule + + + + + + + + Logo +
+
+

Upload a schedule

+
+ + +
+

+ + + diff --git a/fdhview/main.py b/fdhview/main.py new file mode 100644 index 0000000..64e9d18 --- /dev/null +++ b/fdhview/main.py @@ -0,0 +1,26 @@ +from contextlib import asynccontextmanager +from pathlib import Path + +from fastapi import FastAPI +from fastapi.staticfiles import StaticFiles + +from fdhview.api.admin import router as admin_router +from fdhview.api.screenings import router as screenings_router +from fdhview.core.database import init_db + + +@asynccontextmanager +async def lifespan(app: FastAPI): + """Runs on startup: ensure DB tables exist""" + await init_db() + yield + + +app = FastAPI(title="Filmhuis Den Haag Signage", lifespan=lifespan) + +BASE_DIR = Path(__file__).resolve().parent + +app.mount("/static", StaticFiles(directory=BASE_DIR / "static"), name="static") + +app.include_router(admin_router) +app.include_router(screenings_router, prefix="/api") diff --git a/fdhview/models/__init__.py b/fdhview/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fdhview/models/enums.py b/fdhview/models/enums.py new file mode 100644 index 0000000..9dcb6d1 --- /dev/null +++ b/fdhview/models/enums.py @@ -0,0 +1,28 @@ +from enum import Enum + + +class KijkWijzer(str, Enum): + ZES = "6" + NEGEN = "9" + TWAALF = "12" + VEERTIEN = "14" + ZESTIEN = "16" + ACHTTIEN = "18" + ALLE = "al" + ANGST = "angst" + DISCRIMINATIE = "discriminatie" + DRUGS = "drugs-en-alcohol" + GROF = "taal" + GEDRAG = "gevaarlijk-gedrag" + GEWELD = "geweld" + SEKS = "seks" + + +class Screen(str, Enum): + ONE = "1" + TWO = "2" + THREE = "3" + FOUR = "4" + FIVE = "5" + SIX = "6" + TEN = "10" diff --git a/fdhview/models/screening.py b/fdhview/models/screening.py new file mode 100644 index 0000000..085eb0e --- /dev/null +++ b/fdhview/models/screening.py @@ -0,0 +1,35 @@ +from datetime import datetime + +import sqlalchemy as sa +from pydantic import field_serializer, field_validator +from sqlmodel import Field, SQLModel + +from fdhview.models.enums import KijkWijzer, Screen + + +class Screening(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + session: int + screen: Screen = Field(default=Screen.ONE, sa_column=sa.Column(sa.Enum(Screen))) + complex_ident: str + start: datetime + end: datetime + duration: int + title: str + rating: int + seats_total: int + seats_sold: int + en_subs: bool = False + intro: bool = False + outro: bool = False + kijkwijzer: list[KijkWijzer] = Field(default_factory=list, sa_type=sa.JSON) + tags: list[str] = Field(default_factory=list, sa_type=sa.JSON) + + @field_validator("kijkwijzer", mode="before") + @classmethod + def _coerce(cls, v): + return [r.value if isinstance(r, KijkWijzer) else r for r in v] + + @field_serializer("kijkwijzer") + def _serialize(self, v: list[KijkWijzer]) -> list[str]: + return [r.value if isinstance(r, KijkWijzer) else r for r in v] diff --git a/fdhview/models/tags.py b/fdhview/models/tags.py new file mode 100644 index 0000000..42cc74c --- /dev/null +++ b/fdhview/models/tags.py @@ -0,0 +1,60 @@ +from fdhview.models.enums import KijkWijzer, Screen + +screens = { + 'DenHaag_zaal_1': Screen.ONE, + 'DenHaag_zaal_2': Screen.TWO, + 'DenHaag_zaal_3': Screen.THREE, + 'DenHaag_zaal_4': Screen.FOUR, + 'DenHaag_zaal_5': Screen.FIVE, + 'DenHaag_zaal_6': Screen.SIX, + 'Filmhuis Den Haag 10 (buitenbios)': Screen.TEN +} + + +kijkwijzer = { + 'vanaf 6 jaar': KijkWijzer.ZES, + 'vanaf 9 jaar': KijkWijzer.NEGEN, + 'vanaf 12 jaar': KijkWijzer.TWAALF, + 'vanaf 14 jaar': KijkWijzer.VEERTIEN, + 'vanaf 16 jaar': KijkWijzer.ZESTIEN, + 'vanaf 18 jaar': KijkWijzer.ACHTTIEN, + 'Alle leeftijden': KijkWijzer.ALLE, + 'Angst': KijkWijzer.ANGST, + 'Discriminatie': KijkWijzer.DISCRIMINATIE, + 'Drugs- en/of alcoholmisbruik': KijkWijzer.DRUGS, + 'Grof taalgebruik': KijkWijzer.GROF, + 'Gevaarlijk gedrag': KijkWijzer.GEDRAG, + 'Geweld': KijkWijzer.GEWELD, + 'Seks': KijkWijzer.SEKS +} + + +extra_tags = [ + '2D', + 'Action', + 'Adventure', + 'Animation', + 'Biographie', + 'Comedy', + 'Crime', + 'Documentary', + 'Drama', + 'EN subs', + 'Family film', + 'Family', + 'Fantasy', + 'Festival', + 'Horror', + 'Kids film', + 'Kids', + 'LGBTQ+', + 'Met muziek', + 'Romantic', + 'Science fiction', + 'Sciencefiction', + 'Thriller', + 'Voorfilm', + 'War', + 'with after talk', + 'with introduction' +] diff --git a/fdhview/schemas/__init__.py b/fdhview/schemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fdhview/schemas/screening.py b/fdhview/schemas/screening.py new file mode 100644 index 0000000..7aabd9a --- /dev/null +++ b/fdhview/schemas/screening.py @@ -0,0 +1,21 @@ +from datetime import datetime + +from sqlmodel import SQLModel + +from fdhview.models.enums import KijkWijzer, Screen + + +class ScreeningRead(SQLModel): + screen: Screen + start: datetime + end: datetime + duration: int + title: str + rating: int + seats_total: int + seats_sold: int + en_subs: bool = False + intro: bool = False + outro: bool = False + kijkwijzer: list[KijkWijzer] = [] + tags: list[str] = [] diff --git a/fdhview/services/__init__.py b/fdhview/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fdhview/services/screenwriter/__init__.py b/fdhview/services/screenwriter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fdhview/services/screenwriter/importer.py b/fdhview/services/screenwriter/importer.py new file mode 100644 index 0000000..68c791f --- /dev/null +++ b/fdhview/services/screenwriter/importer.py @@ -0,0 +1,96 @@ +from sqlmodel import delete +from sqlmodel.ext.asyncio.session import AsyncSession + +from fdhview.models.enums import Screen +from fdhview.models.screening import KijkWijzer, Screening +from fdhview.models.tags import kijkwijzer, screens +from fdhview.services.screenwriter.schema import AAMPos + +# Delimiters used to attach notes to titles +DASH_DELIMITERS = (" - ", " – ", " — ", "- ") + +# Explicit list of known screening notes (exact matches only) +KNOWN_SCREENING_NOTES = frozenset({ + # Subtitles + "en subs", + "en sub", + "nl subs", + "nl sub", + + # Events & talks + "met inleiding", + "met nagesprek", + "met inleiding en nagesprek", + "met voorfilms en nagesprek", + + # Combined notes + "en subs met inleiding", + "en subs en nagesprek", +}) + + +def _clean_title(title: str) -> str: + """Peel off trailing presentation notes using an explicit blacklist.""" + for dash in DASH_DELIMITERS: + while dash in title: + prefix, _, suffix = title.rpartition(dash) + # Exact string check (case-insensitive and trimmed) + if suffix.strip().lower() in KNOWN_SCREENING_NOTES: + title = prefix.strip() + else: + break + + return title.strip() + + +async def import_sessions(data: AAMPos, session: AsyncSession): + # purge old rows + await session.exec(delete(Screening)) + + # add new screenings from data + for screening in data.sessions.root: + + kijk: list[KijkWijzer] = [] + for k in kijkwijzer: + if k in screening.attributes: + kijk.append(kijkwijzer[k]) + screening.attributes.remove(k) + + if "EN subs" in screening.attributes: + en_subs = True + screening.attributes.remove("EN subs") + else: + en_subs = False + + if "with introduction" in screening.attributes: + intro = True + screening.attributes.remove("with introduction") + else: + intro = False + + if "with after talk" in screening.attributes: + outro = True + screening.attributes.remove("with after talk") + else: + outro = False + + new_screening = Screening( + session=screening.session_id, + screen=screens.get(screening.screen_identifier, Screen.ONE), + complex_ident=screening.complex_identifier, + start=screening.start, + end=screening.end, + duration=screening.feature_duration, + title=_clean_title(screening.title), + rating=screening.rating, + seats_total=screening.seats_available, + seats_sold=screening.seats_sold, + en_subs=en_subs, + intro=intro, + outro=outro, + kijkwijzer=kijk, + tags=screening.attributes + ) + + session.add(new_screening) + await session.commit() diff --git a/fdhview/services/screenwriter/parser.py b/fdhview/services/screenwriter/parser.py new file mode 100644 index 0000000..f159968 --- /dev/null +++ b/fdhview/services/screenwriter/parser.py @@ -0,0 +1,17 @@ +from xml.etree import ElementTree as ET + +from pydantic import ValidationError + +from fdhview.services.screenwriter.schema import AAMPos + + +class XmlParseError(Exception): + """Raised when an XML is malformed or violates the ScreenWriter schema.""" + + +def parse_screenwriter_xml(content: bytes | str) -> AAMPos: + """Parser raw ScreenWriter XML into validated Pydantic models.""" + try: + return AAMPos.from_xml(content) + except (ValidationError, ET.ParseError) as e: + raise XmlParseError(f"Failed to parse ScreenWriter XML: {e}") from e diff --git a/fdhview/services/screenwriter/schema.py b/fdhview/services/screenwriter/schema.py new file mode 100644 index 0000000..eb18cc6 --- /dev/null +++ b/fdhview/services/screenwriter/schema.py @@ -0,0 +1,34 @@ +from datetime import datetime +from typing import Annotated +from zoneinfo import ZoneInfo + +from pydantic import BeforeValidator +from pydantic_xml import BaseXmlModel, RootXmlModel, element, wrapped + +from fdhview.core.config import settings + +DT = Annotated[datetime, BeforeValidator(lambda v: datetime.strptime(v, "%Y%m%d%H%M").replace( + tzinfo=ZoneInfo(settings.timezone) +))] + + +class Session(BaseXmlModel, tag='session'): + session_id: int = element(tag='id') + screen_identifier: str = element() + complex_identifier: str = element() + start: DT = element() + end: DT = element() + feature_duration: int = element() + title: str = element() + rating: int = element() + seats_available: int = element() + seats_sold: int = element() + attributes: list[str] = wrapped('session_attributes', element(tag='session_attribute')) + + +class Sessions(RootXmlModel, tag='sessions'): + root: list[Session] + + +class AAMPos(BaseXmlModel, tag='AAMPos'): + sessions: Sessions = element() diff --git a/fdhview/static/12.png b/fdhview/static/12.png new file mode 100644 index 0000000000000000000000000000000000000000..047e11cc94b7a3d34a0ca5ac3f28d26cc12c1602 GIT binary patch literal 3189 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 bU%vkX$0b&(96d_F00000NkvXXu0mjfQ#9)n literal 0 HcmV?d00001 diff --git a/fdhview/static/14.png b/fdhview/static/14.png new file mode 100644 index 0000000000000000000000000000000000000000..ec4b9bf7d723cf3b8d4aaa1b7e26e12c362f4700 GIT binary patch literal 2853 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 DmhL@D literal 0 HcmV?d00001 diff --git a/fdhview/static/16.png b/fdhview/static/16.png new file mode 100644 index 0000000000000000000000000000000000000000..b02828d34c6bc3c8954db96d3eeb367d8a70607b GIT binary patch literal 2840 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 + + + + + + + + + + + + + + + + + + diff --git a/fdhview/static/al.png b/fdhview/static/al.png new file mode 100644 index 0000000000000000000000000000000000000000..9127eb6148b1754d53a764425c0c773ce55efee1 GIT binary patch literal 2635 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 literal 0 HcmV?d00001 diff --git a/fdhview/static/angst.png b/fdhview/static/angst.png new file mode 100644 index 0000000000000000000000000000000000000000..eedbe5a1c1d06f251a35f74374df0261788e0461 GIT binary patch literal 4322 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 Q>Hq)$07*qoM6N<$g1yUnEdT%j literal 0 HcmV?d00001 diff --git a/fdhview/static/discriminatie.png b/fdhview/static/discriminatie.png new file mode 100644 index 0000000000000000000000000000000000000000..0a6c3af987b73bd4a990103a77383e690edd4026 GIT binary patch literal 3771 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 literal 0 HcmV?d00001 diff --git a/fdhview/static/drugs-en-alcohol.png b/fdhview/static/drugs-en-alcohol.png new file mode 100644 index 0000000000000000000000000000000000000000..4f07b6842857e54a5500c7f4527fde9f9505b298 GIT binary patch literal 3189 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 bbm{&F*21#FD^1)T00000NkvXXu0mjf&|WsM literal 0 HcmV?d00001 diff --git a/fdhview/static/gevaarlijk-gedrag.png b/fdhview/static/gevaarlijk-gedrag.png new file mode 100644 index 0000000000000000000000000000000000000000..6f406d7c36c05efc73c4b912eedca25e5c5b52ff GIT binary patch literal 3982 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 UFdbeAF#rGn07*qoM6N<$g7IAUSpWb4 literal 0 HcmV?d00001 diff --git a/fdhview/static/seks.png b/fdhview/static/seks.png new file mode 100644 index 0000000000000000000000000000000000000000..0d995754e144b1fdd7778a5afcbea3908686c1fe GIT binary patch literal 4063 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 literal 0 HcmV?d00001 diff --git a/fdhview/static/style.css b/fdhview/static/style.css new file mode 100644 index 0000000..2088656 --- /dev/null +++ b/fdhview/static/style.css @@ -0,0 +1,69 @@ +:root { + --bg: #dfe2e9; + --bg-side: #d6dae2; + --ink: #111318; + --muted: #8a8f99; + --rule: #9aa0aa; + --badge: #101318; +} +* { margin: 0; padding: 0; box-sizing: border-box; } +html, body { height: 100%; } +body { + font-family: "Poppins", "Futura", sans-serif; + background: var(--bg); + color: var(--ink); + overflow: hidden; +} +.screen { display: grid; grid-template-columns: 1fr 340px; height: 100vh; } + +/* ---------- left: film list ---------- */ +.programme { padding: 28px 40px 40px 48px; display: flex; flex-direction: column; } +.programme h1 { + font-size: 92px; font-weight: 600; letter-spacing: 6px; + text-transform: uppercase; line-height: 1; margin-bottom: 18px; +} +.film { + display: grid; + grid-template-columns: 90px 60px 1fr 170px; + gap: 12px; align-items: start; + padding: 16px 0 18px; + border-top: 1px solid var(--rule); +} +.times { line-height: 1.25; } +.times .start { font-size: 20px; font-weight: 600; } +.times .end { font-size: 20px; color: var(--muted); } +.icons { display: flex; gap: 6px; padding-top: 3px; } +.icons .dot { + width: 26px; height: 26px; border-radius: 50%; + background: var(--badge); color: #fff; + font-size: 13px; font-weight: 600; + display: flex; align-items: center; justify-content: center; +} +.info { padding-top: 3px; } +.info .title { font-size: 22px; font-weight: 500; } +.info .meta { + margin-top: 14px; font-size: 13px; font-weight: 600; + letter-spacing: 1.5px; text-transform: uppercase; + display: flex; gap: 18px; align-items: center; +} +.pill { + background: #b9bfc9; border-radius: 14px; + padding: 4px 14px; font-size: 12px; font-weight: 600; + letter-spacing: 1px; text-transform: uppercase; +} +.badge-last { + justify-self: end; align-self: start; margin-top: 3px; + background: var(--badge); color: #fff; + font-size: 11px; font-weight: 600; letter-spacing: 1.5px; + padding: 6px 12px; text-transform: uppercase; white-space: nowrap; +} + +/* ---------- right: sidebar ---------- */ +.sidebar { background: var(--bg-side); padding: 30px 36px; display: flex; flex-direction: column; } +.sidebar .top { display: flex; justify-content: space-between; align-items: flex-start; } +.sidebar .date { font-size: 44px; font-weight: 600; letter-spacing: 4px; } +.sidebar .logo { font-size: 13px; font-weight: 600; letter-spacing: 1px; text-align: right; line-height: 1.3; text-transform: uppercase; } +.sidebar h2 { margin-top: 56px; font-size: 20px; font-weight: 600; margin-bottom: 14px; } +.price-row { display: flex; justify-content: space-between; gap: 16px; margin-bottom: 9px; font-size: 17px; } +.price-row .price { white-space: nowrap; } +.sidebar h3 { margin-top: 34px; font-size: 17px; font-weight: 600; letter-spacing: 1px; text-transform: uppercase; margin-bottom: 14px; } diff --git a/fdhview/static/taal.png b/fdhview/static/taal.png new file mode 100644 index 0000000000000000000000000000000000000000..6990c8209d50a40fde8b4ff3fc25d193f4f00828 GIT binary patch literal 3549 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 XT34I~qz#X`00000NkvXXu0mjfHf!0m literal 0 HcmV?d00001 diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..0c96c61 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1181 @@ +# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. + +[[package]] +name = "aiosqlite" +version = "0.22.1" +description = "asyncio bridge to the standard sqlite3 module" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiosqlite-0.22.1-py3-none-any.whl", hash = "sha256:21c002eb13823fad740196c5a2e9d8e62f6243bd9e7e4a1f87fb5e44ecb4fceb"}, + {file = "aiosqlite-0.22.1.tar.gz", hash = "sha256:043e0bd78d32888c0a9ca90fc788b38796843360c855a7262a532813133a0650"}, +] + +[package.extras] +dev = ["attribution (==1.8.0)", "black (==25.11.0)", "build (>=1.2)", "coverage[toml] (==7.10.7)", "flake8 (==7.3.0)", "flake8-bugbear (==24.12.12)", "flit (==3.12.0)", "mypy (==1.19.0)", "ufmt (==2.8.0)", "usort (==1.0.8.post1)"] +docs = ["sphinx (==8.1.3)", "sphinx-mdinclude (==0.6.2)"] + +[[package]] +name = "annotated-doc" +version = "0.0.5" +description = "Document parameters, class attributes, return types, and variables inline, with Annotated." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101"}, + {file = "annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb"}, +] + +[[package]] +name = "annotated-types" +version = "0.8.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0"}, + {file = "annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7"}, +] + +[[package]] +name = "anyio" +version = "4.15.1" +description = "High-level concurrency and networking framework on top of asyncio or Trio" +optional = false +python-versions = ">=3.10" +groups = ["main", "dev"] +files = [ + {file = "anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101"}, + {file = "anyio-4.15.1.tar.gz", hash = "sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94"}, +] +markers = {dev = "sys_platform != \"emscripten\""} + +[package.dependencies] +idna = ">=2.8" +typing_extensions = {version = ">=4.16.0", markers = "python_version < \"3.15\""} + +[package.extras] +trio = ["trio (>=0.32.0)"] + +[[package]] +name = "ast-serialize" +version = "0.13.0" +description = "Python bindings for mypy AST serialization" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ast_serialize-0.13.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:4d1e15da4b6afc6fe80b87704be452aa0639df93d019a516e9ac9540357fc9b2"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:619050b18705310e19e254cdb7554289fe14da374cbfbb1362cd84635896fb7f"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7ce1b50c5a68233e890926405afc308a5f10f6f49ec3a094d3dfa8b6733e4496"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bab08a6f287cd620578084f9974cfaf3bef71959105f62af85fa70298b24851"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e0bc018a457052d4638b469f90674e6ec0e32d86ac4a7bfa1f7d1c71a961426"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:452fdaf5ff0b791870bb332254e083107d7abe29ef43251411c265c5b138f9a2"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12441bc7e41e495db5634c98adc8f8886b619ce2f1e68effe3f792a2adca9f47"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84cd9efdd3cd780b1f5361049becd91e0bcb0f16c2c216f41ee82e728a98390b"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4dc7a24c734aded0557ef90bfabd2278b37502aacde84f49b53b4cb6a0711ea9"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c95c04f1781cbefe89d512ce30e051d10823827ae542d9f18d5c2e7ba0fad08"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fadba24386498ed745c848b0d45e4939a506694bd2b474c57576e9640f3defe2"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:771cf5ee8329ee8472dd7a3d8bea7dbc480032ed8ddb4d37d40b57b95ee19ef2"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a1714ee591a8e19833c0530a89e5a9faa7f62a11fc88f62fc5b722c7425dcc1f"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:220a993dfc8b173e7062f690f9e00f4ebefe56718171bf35dccd73a9f8cea100"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:446de6067d853f61b4bde8741762c83d06b57ea81f96dd47977714d9f31837fa"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f0cc1f94fcd3b67005a32ee3e4c6b27cdc41659f697840d00fbb1e815ec27044"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:77efef815ecae1195ac9889f616bd518d53b2173e87157043253b864af1c81c5"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c77e5b62dfbfdfc1b025105f5038114ae988a0e616d48a845e0e57173f3f37c8"}, + {file = "ast_serialize-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:8e7c6fec7fe03cb8f40c4af81d742aa0cf690cf0b7bded47508a8a392093f414"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-macosx_10_12_x86_64.whl", hash = "sha256:9eaa20714acf43ef0a0c82850a2ec8097f834648f527c38f1483e2fd9a52cd3e"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:3f5d4a7fcc916026010bae2a154e5be2c05040e67ef5c494c66a5cf315c41e30"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:807875ed8c5de739c8c55a45944336fcb9b8601d77fcee384a743cd0497211d6"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e611937e6e77448496489627ae2558b6f6143449b1fb33f8a495665212eee58a"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bb8dd779c0a25478fe1db1a8b06dd4dd5e66077d6d0afe354acadb6d9aee7d0"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a49f2a01a6df3150e022087cec0bf0ad580fec8f38a17f124d07dbb115106d1"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f30f0e59be30c0c9540e8908b14874bc8d3c1d52a4562a4cfb426b003bd6c28b"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-manylinux_2_31_riscv64.whl", hash = "sha256:be6b1a4ee49866c77eb8a50e9cbc845370e6c15230a126d0a71aeab35f31c78c"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8da1a31e941adbea886a85fb25efc6f09353d58c665fdbc523a914e3d2e49fe"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:3a9469e4b93d87e4ee8f5c7e9462f973032793a24b9ae37ffee860220f17586a"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-musllinux_1_2_armv7l.whl", hash = "sha256:192aed400b2b92ebe41da17e856b9b6e17dbcebe0011ce4c6370d4a8a0486233"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-musllinux_1_2_i686.whl", hash = "sha256:ee58f0db40f121ff0820242b286702700bc0ec58a53b6ac43ce4f43714d42e0d"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-musllinux_1_2_ppc64le.whl", hash = "sha256:e241f68cf5060bff9b161b202b60d6d52161ff3777fe56eb6a9a6764fdb7fdd9"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-musllinux_1_2_riscv64.whl", hash = "sha256:bd89da715b857a27c33fad713ea0561912c56f773ebd0fed2760cedd99724dc6"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:6cbfa6dae34d5686056ef7c40ce1d3e7e1de48555e3a2fed985aef2d1f869d9a"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-win32.whl", hash = "sha256:6a406251363eeb5c7b85a405eddd123e627a531bd150dc673a7f5dd087743b5c"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:cdd8fd066858b57ea2761b3d3989c90ea23913825bbb5453cc684c28bba3fb19"}, + {file = "ast_serialize-0.13.0-cp315-abi3.abi3t-win_arm64.whl", hash = "sha256:841262622499585f0610a927434db526578553d8dae270b90d4c419a385e46b7"}, + {file = "ast_serialize-0.13.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:efaab6400de8ee2d0e38695feccb0758acf11c1d0f8bc58a7090c566dd3ae88e"}, + {file = "ast_serialize-0.13.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c51c855d8b7d5403925599acd3c6fc91b321eba3bf46dcc9fe88fd2d6619dac7"}, + {file = "ast_serialize-0.13.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:d47c8f0eedf0a41681c10a7c497fef3691c4a6b4af4de6c93a4916bb29712554"}, + {file = "ast_serialize-0.13.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d7376c611055f5ee44e5e13a2620dbc6846f47c583952c1704c8805154c2d2f"}, + {file = "ast_serialize-0.13.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fe2a3c8480e8e5eb41eaa958279b8c530b77b45065423f4ebd5223e118293055"}, + {file = "ast_serialize-0.13.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98edcd24240fa217d903c8f221bc05575baf38a87a207264ee7c800d21eef5a4"}, + {file = "ast_serialize-0.13.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19b1e8f4c088ce91053df310b444fceeddb39f728ca7d04272c983646e36314b"}, + {file = "ast_serialize-0.13.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f55668338bcb871e83ee21ba865fb08b7b4b13af9312742f9878c39f9d84ee3"}, + {file = "ast_serialize-0.13.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:a918572608ceb20fba8c2b83560f3d20be90effa4614589477b46d81672f0c3d"}, + {file = "ast_serialize-0.13.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b004c3bdab0beb45194cd66c0b8feea40d676e461d26296f9c791c8e3e1b7061"}, + {file = "ast_serialize-0.13.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5e88733df5ffff9062b5ff2779cf402e0aa1a61ca3f7f84b577a1af2b7e09676"}, + {file = "ast_serialize-0.13.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:017ddd4f22e727ef93e66df2d53340a6ff809b7e34cc2218f67918ae6239aad0"}, + {file = "ast_serialize-0.13.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:4b2ee61692de03009e6a8f372f24fbc2d4b768a2f51ecd426bb84acdfd6da3d1"}, + {file = "ast_serialize-0.13.0-cp39-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:dffcffa543c8fcfb1ca941038eeae23e7f97ad8994e4d6f81fbd658cfa8cb440"}, + {file = "ast_serialize-0.13.0-cp39-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:3e20e9ca3952196b91798f77ef267c36c7b3470021f950aa361d9be003fb655f"}, + {file = "ast_serialize-0.13.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:359fcebc49f855bd189cf568235dc84eabf521e00d03fce43135cad6484906bc"}, + {file = "ast_serialize-0.13.0-cp39-abi3-win32.whl", hash = "sha256:684e191dd41b08b0b92692a181380a70cd76e3b6469606a40aae1ca6dab39e7d"}, + {file = "ast_serialize-0.13.0-cp39-abi3-win_amd64.whl", hash = "sha256:8f672c8e6d3b9ef6e365a5543aee2012247d1d58d948ceddb75b33a6679609ca"}, + {file = "ast_serialize-0.13.0-cp39-abi3-win_arm64.whl", hash = "sha256:8aff1682f9fa3e119a1cf8ef47504d38f7019b22b79e0f2b5c2135b9532d14db"}, + {file = "ast_serialize-0.13.0.tar.gz", hash = "sha256:a0bdcef01e643e0810d2dedfb64d924bcfe079a15dc20d1c067870cc01d5c5e6"}, +] + +[[package]] +name = "click" +version = "8.5.0" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360"}, + {file = "click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "fastapi" +version = "0.141.1" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "fastapi-0.141.1-py3-none-any.whl", hash = "sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3"}, + {file = "fastapi-0.141.1.tar.gz", hash = "sha256:e8822fc40db1e1858054d7a949a888695bc9bdce70139178e33bd2871a453ca1"}, +] + +[package.dependencies] +annotated-doc = ">=0.0.2" +pydantic = ">=2.9.0" +starlette = ">=0.46.0" +typing-extensions = ">=4.8.0" +typing-inspection = ">=0.4.2" + +[package.extras] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.32)", "httpx (>=0.23.0,<1.0.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.32)", "fastar (>=0.9.0)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] +standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[standard-no-fastapi-cloud-cli] (>=0.0.32)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "greenlet" +version = "3.5.6" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.10" +groups = ["main"] +markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\"" +files = [ + {file = "greenlet-3.5.6-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:95e7c44d072db623a1aab04ce488cf9533294a77ed9d072cd503a3596f4106ac"}, + {file = "greenlet-3.5.6-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b7d501d5eb5d4f67207df364752ad697465b834268744be7581c18d81d35d41d"}, + {file = "greenlet-3.5.6-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a364c1ea75dc51b83a17f52fe0c79cf8bc4ddf740403bebd4581c7666eea017d"}, + {file = "greenlet-3.5.6-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5599b380c1f28efeb724e81569eac80cd92f99a85bd9775456caaf3225d40b11"}, + {file = "greenlet-3.5.6-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eed88b64a5e5da72d6a71cdc5aaeefaa5ced9b748f8d19f89800b339961dad39"}, + {file = "greenlet-3.5.6-cp310-cp310-manylinux_2_39_riscv64.whl", hash = "sha256:5bbda3c70dd35d60671bc33b01916802707a052130d9e50cdb871d34594d35cb"}, + {file = "greenlet-3.5.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:874cea8bb1ec1ddccbacbd027856f6bf496f6bc18aba97a918c20e067edab236"}, + {file = "greenlet-3.5.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:128813fc29f2336a21b4d06eedd5e16bcc7ea46f59e9ff1cb30ea70e48195d88"}, + {file = "greenlet-3.5.6-cp310-cp310-win_amd64.whl", hash = "sha256:dad3d233d441a022c1f7155f0fb9d5aff7b97c1ea8c7dfa02cce586b16ab2d0b"}, + {file = "greenlet-3.5.6-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:a6a4b98a9132e0f45c9fc245a63894cfd8c45fb7a0d6bffc5eab3ec327cf7324"}, + {file = "greenlet-3.5.6-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45bfd2b51e38aaa5f9849f114d9c7c1d75f69187c849b3549cd64c465283abfa"}, + {file = "greenlet-3.5.6-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3c6dede9133e1da41d561bc3fb14e92b47e2ce39ae60edefaad145658ea7c5e2"}, + {file = "greenlet-3.5.6-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4fb8e59f68845d56c23c031dcd79c329f345e4a9d2ffac91c3d1ab366bdc457b"}, + {file = "greenlet-3.5.6-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c20ea32a73d17b9b60e3371240e17b0068120c98a5ec01a224a7dd8c89733ba"}, + {file = "greenlet-3.5.6-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:d701eab36200c36224833d07dbdb709adb7fd4253429548ddb5e547b8ed40586"}, + {file = "greenlet-3.5.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a0b2791239c99992a86c1b635b787fe2a877d9eaaa26f8891ce943832b585ae"}, + {file = "greenlet-3.5.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:188bf333769b7145e2b0b4a7f09615ec550ed44d3a2a8395fb7b36f0e9901e13"}, + {file = "greenlet-3.5.6-cp311-cp311-win_amd64.whl", hash = "sha256:a6b4ff33f7e011bbaa148238d131c4fd4f8afbab3c104ddfbdb2b12b74ff7016"}, + {file = "greenlet-3.5.6-cp311-cp311-win_arm64.whl", hash = "sha256:59deccd347735a7774223b05a93773fddbb298aba3cea21be4337fb4752dbe32"}, + {file = "greenlet-3.5.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:a5876d0a60355af98d535c47f6cd6eb0f8a432396dab26845d380b92f8412422"}, + {file = "greenlet-3.5.6-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e85880b538e59a59f55117b81f208a6660ad5ac328aad9305f812d9b8bc67a0f"}, + {file = "greenlet-3.5.6-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f0ba7c2a329d650628f4c8572fd1db29f0a59dd70a3e3e0710dcf18a35cce9d8"}, + {file = "greenlet-3.5.6-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ee7d9da3bf493909cf811a3f038840cb34fab5ae2956b8a263919f6e289ab188"}, + {file = "greenlet-3.5.6-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:975736b002ed080d124cf81a79cb7e05cb26d6b3f5c7a7b651c0fcce70353aa1"}, + {file = "greenlet-3.5.6-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:71890d5247020c25c21a6b65202782bfc281d4e6e244842419d30e3492bb6dcc"}, + {file = "greenlet-3.5.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0616b8f878098c5681fd8f0dc92d887551717402342a70f0abcbfea5f5ad8a44"}, + {file = "greenlet-3.5.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3dbb4596a6a4e5d47121a33ff20533a81e60f302d9e67b69909a8bc21a43f0a7"}, + {file = "greenlet-3.5.6-cp312-cp312-win_amd64.whl", hash = "sha256:7ac4abb3877c43af320392c664774eef6fa2cc063c79a55fc02d844a3cbe7395"}, + {file = "greenlet-3.5.6-cp312-cp312-win_arm64.whl", hash = "sha256:301102a49120b095e72a7838792b41233975fc1c155daec6d98f81c00c9280e0"}, + {file = "greenlet-3.5.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:f96f0e30b5a95c7631b12bfe214cbc90ec8fe8cfa36920596c10514a65743519"}, + {file = "greenlet-3.5.6-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c75116c9de79949de23006e2d9b35ee82874c594fcf5c0311b439acaa14b8441"}, + {file = "greenlet-3.5.6-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cad5782f93f7f738b62c6527b6f32a60694d924029f299a8b524758cfa53d815"}, + {file = "greenlet-3.5.6-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a93ee7c6e8fd0f8a83525a51bd777be57ee17787e91d805bd8d6faf9dcada18e"}, + {file = "greenlet-3.5.6-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f98e8215e172f567ce80eeaed9107fb4d32b6c44f26983d9b8334658136a205a"}, + {file = "greenlet-3.5.6-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:7f731ebac68ea06d628658295cb2d217b10186329fcf9a3b6a149045059bf92e"}, + {file = "greenlet-3.5.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:df19e2d0b1620039af5102563fbd96e8938c7f5c3f5828528d641d9fc585525e"}, + {file = "greenlet-3.5.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:06c0e933290fba8ffe53ead4ae1b8044b0e9754b75cebf381aa2bc3e50d82fac"}, + {file = "greenlet-3.5.6-cp313-cp313-win_amd64.whl", hash = "sha256:5b602b4201b965a8354d74e232364a66ff243dd142e350d035f46169bb36e13d"}, + {file = "greenlet-3.5.6-cp313-cp313-win_arm64.whl", hash = "sha256:876077e7ebb8c84ed068e2b23d4c62ebb010d60df84b9591af1be2f39010ffb2"}, + {file = "greenlet-3.5.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8cddea1b8339451c2fb3388e138347b6126744f33b611bdb55b7357361cfef46"}, + {file = "greenlet-3.5.6-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c59acfa8eb73a1e0d484392dc002bdf001fd4ce73394e0132df3d1ab6093d7cb"}, + {file = "greenlet-3.5.6-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a3b4a01c6da07ef9f80d4fe8933b994bc99747bcea3eab0330a9c34d3c12655b"}, + {file = "greenlet-3.5.6-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dd0b83bed3405b586a3133629f1d1a5bc7bfd64822a3b7ab342bdc68e6dbc61b"}, + {file = "greenlet-3.5.6-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9a09d59bef1db94f384b5bcc2d523694d338f3df6b757aeeaf7baca5d0c0be88"}, + {file = "greenlet-3.5.6-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:fdacf26402389bdd89857ad3c045a26fe8f3314f9a8b28226f82f88463a65b77"}, + {file = "greenlet-3.5.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8b7c73d1cef3d9ae963e9ff03f6222df43efbb9054ffd2f1969c935b7fc84c02"}, + {file = "greenlet-3.5.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8b27df301f56e3b3d2298095c8f7d6b68f2521f6b1693e901fa039bdbae34424"}, + {file = "greenlet-3.5.6-cp314-cp314-win_amd64.whl", hash = "sha256:f8f0bd690e1a41294ac87905e8121c81a3761ec2583c768f13467428606c8c7a"}, + {file = "greenlet-3.5.6-cp314-cp314-win_arm64.whl", hash = "sha256:8cda13494d86a4f12429641117cb6ac4bbbc9c30a33f711f7d3a2e5fbe4b0b7e"}, + {file = "greenlet-3.5.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:97c5a53e8c1754df58e73f047a99e287d4da1bdfe64b0072fb25c87000897951"}, + {file = "greenlet-3.5.6-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fea4427d1ffdb3b523d7daa6712038428a4c16c450b9777bdd1221cfee0eab49"}, + {file = "greenlet-3.5.6-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73a29b5ba642e35433166a03a3e02935e7238c4b3467fbd77523b99edea23e5b"}, + {file = "greenlet-3.5.6-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:61a61b4a95a4f97922c3a6f5606d3e360851584bd47e500a5161373c53810e3d"}, + {file = "greenlet-3.5.6-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:460e70b033aba8ed47e2ac9b5d0d2157b05a34fbfa30a241400aef4118902cdc"}, + {file = "greenlet-3.5.6-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:fe3170a69fe039b18ad18171e66faa9a75f6fe9d78f968fd9b54e09fbd714d81"}, + {file = "greenlet-3.5.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca80a49b53ed1d22f7282da7255f7bb2fd1935fd0f623d8613fda38745f18961"}, + {file = "greenlet-3.5.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:916f92f2a8db10508f739d0b5e00b83defe5d1115a997c54532a6d7cf8c95404"}, + {file = "greenlet-3.5.6-cp314-cp314t-win_amd64.whl", hash = "sha256:886bcf1870af74c32bc310fd00a6b803445e17e51b7d5a107c7b35c0f362cc16"}, + {file = "greenlet-3.5.6-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:3ac3494c381dab876cad7d0b22f3a722f3e0c8deb3a65b9e7f35ad7f58b8fcb3"}, + {file = "greenlet-3.5.6-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:602024dae6d77e161f4b89491b62ca1d4f19949d79d47b2db057e476d21179d6"}, + {file = "greenlet-3.5.6-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f8e63209c3e1e828ee6a457529b4a6d8b05d050fe0ae03a7ae49e967c5d312e0"}, + {file = "greenlet-3.5.6-cp315-cp315-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9133d68624b1f2e89ec2f554d56aea8a5b0d7168cd9320200ba58d4d794845a4"}, + {file = "greenlet-3.5.6-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccadce0130fd813ec86ebfe969a6c58b42acc1d0fe55a47525375b740e07b605"}, + {file = "greenlet-3.5.6-cp315-cp315-manylinux_2_39_riscv64.whl", hash = "sha256:5adcbbfe78bdc242c71740a02e0991cc1b2f34d33c8bb15ca45eee8fd1140942"}, + {file = "greenlet-3.5.6-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:9297fb9c39b9a2c039dbcd306c410bd6906b95244dec3bba4318d36c718c164c"}, + {file = "greenlet-3.5.6-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:b374e79ffa7511afc11773aef40a4ccea6191fba1c856ea2f9c56738dca69d7a"}, + {file = "greenlet-3.5.6-cp315-cp315-win_amd64.whl", hash = "sha256:7969bffa322c097bd46ae595ada6a931cefda613f18ba64587e9cff4cb320756"}, + {file = "greenlet-3.5.6-cp315-cp315-win_arm64.whl", hash = "sha256:8dba0129b93e7091dfefaf4cf7000172741bff7f47bf6326fcf17f32fbb54d6b"}, + {file = "greenlet-3.5.6-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:de3de000d459402cda015068fd135aa50c0bf6f2477a80d4da1e646f123b4e78"}, + {file = "greenlet-3.5.6-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45663c01a4de48b9a64a2ee1509d92d1dfd3afb02b2ccfc9333029d11aef996a"}, + {file = "greenlet-3.5.6-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3deccbb57a481e3a408fe61cdfd5c13e0678fc0a30fdd09597917ca87b4be877"}, + {file = "greenlet-3.5.6-cp315-cp315t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:63aff70fe5aac59c72215f42ec39fcb59ff46774fa966e717f8ecb6ee2273577"}, + {file = "greenlet-3.5.6-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:311018b46472fb26ee85870847fb89eb64cc8aaddb617400789d87076f7cfeec"}, + {file = "greenlet-3.5.6-cp315-cp315t-manylinux_2_39_riscv64.whl", hash = "sha256:520648db8fb92eef7b3e6013f5a6f901cdf0d6685f639c2f7a245879f865bef7"}, + {file = "greenlet-3.5.6-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:7f924a5a9d5890649566f2f6682e0d8ad8ca23028bacffbbac36dbd7fd680176"}, + {file = "greenlet-3.5.6-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:de9923832f2d8c1a5ecd8d7260465a6ca5a86888a0d129e3bd5cf0406d2fc5bf"}, + {file = "greenlet-3.5.6-cp315-cp315t-win_amd64.whl", hash = "sha256:2ab5f42ac6c238eb71770715e6e909ad9a1a92b6c681ccb64cd5a0f07edb953f"}, + {file = "greenlet-3.5.6-cp315-cp315t-win_arm64.whl", hash = "sha256:f9fe868463ec7e1363733af77e38a5fda3e9b63940337048c945d69e0c80ff24"}, + {file = "greenlet-3.5.6.tar.gz", hash = "sha256:8e67c43bdfc88d5fee6db0d3e40175b362fc95fb85f0412d233b9b203c53a575"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil", "setuptools"] + +[[package]] +name = "h11" +version = "0.16.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, +] +markers = {dev = "sys_platform != \"emscripten\""} + +[[package]] +name = "httpcore2" +version = "2.13.1" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +markers = "sys_platform != \"emscripten\"" +files = [ + {file = "httpcore2-2.13.1-py3-none-any.whl", hash = "sha256:e1e05d4f25f7d7d496bfb96748f6f4b67657b03da069b3a68c36069f3db73d0a"}, + {file = "httpcore2-2.13.1.tar.gz", hash = "sha256:e0aa977abe17e69a3b820a24542a6fa88702676d83880b8d194dcd18408e5103"}, +] + +[package.dependencies] +h11 = ">=0.16" +truststore = ">=0.10" + +[package.extras] +asyncio = ["anyio (>=4.5.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.34.0,<1.0)"] + +[[package]] +name = "httpx2" +version = "2.13.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "httpx2-2.13.1-py3-none-any.whl", hash = "sha256:6dff50fabc270ee5fd25d845d0b078ed20564579744d6d962850975996d2f9a4"}, + {file = "httpx2-2.13.1.tar.gz", hash = "sha256:e48744a19e3af5ee48313d0ce5fe941d5422fae5705ea922a4aabf94d7800dfa"}, +] + +[package.dependencies] +anyio = {version = ">=4.10", markers = "sys_platform != \"emscripten\""} +httpcore2 = {version = "2.13.1", markers = "sys_platform != \"emscripten\""} +httpx2-jsfetch = {version = "*", markers = "sys_platform == \"emscripten\" and python_version >= \"3.12\""} +idna = ">=3.18" +truststore = {version = ">=0.10", markers = "sys_platform != \"emscripten\""} + +[package.extras] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.2) ; platform_python_implementation != \"CPython\""] +cli = ["click (>=8.4.2)", "pygments (==2.*)", "rich (>=10,<16)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +ws = ["wsproto (>=1.2)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version <= \"3.13\""] + +[[package]] +name = "httpx2-jsfetch" +version = "1.0" +description = "httpx2 transports for Emscripten/Pyodide, backed by the JavaScript fetch API." +optional = false +python-versions = ">=3.12" +groups = ["dev"] +markers = "sys_platform == \"emscripten\"" +files = [ + {file = "httpx2_jsfetch-1.0-py3-none-any.whl", hash = "sha256:cb916b707601e69a07721aabc8f3f6659be3a6893bc1ff5c6f9e02241df2da32"}, + {file = "httpx2_jsfetch-1.0.tar.gz", hash = "sha256:70a0e3eabfef7cce5ad9c629f7d01ca05e418f586646f4ddf14782e4c1454c60"}, +] + +[[package]] +name = "idna" +version = "3.20" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "idna-3.20-py3-none-any.whl", hash = "sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c"}, + {file = "idna-3.20.tar.gz", hash = "sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44"}, +] + +[package.extras] +all = ["coverage (>=7.10.0)", "hypothesis (>=6.141.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.16.0)", "ty (>=0.0.37)"] + +[[package]] +name = "iniconfig" +version = "2.3.1" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "iniconfig-2.3.1-py3-none-any.whl", hash = "sha256:9121e2c1fdb355232495be3194c8dfe87ccc2d5dee45947b78e68f499790d7a7"}, + {file = "iniconfig-2.3.1.tar.gz", hash = "sha256:67f4b9c50da0dedf52af349e7749a80a9057a5031199791b906c3bb3ae878960"}, +] + +[[package]] +name = "librt" +version = "0.16.0" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "librt-0.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:96f576f2711f8519152ec76d0e599243555c1f07679fa73606ca8c8c868c0be6"}, + {file = "librt-0.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bec3818c7da7c96ceae0ef5915a3d16c52dd08f3ea913bf1fe8568c447c7978"}, + {file = "librt-0.16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64c79520414a3fdfc6aabd7593e6169afa14d5f8d9908d4b498db068868b08dd"}, + {file = "librt-0.16.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f81b5b19ce748ef68d4746656b7929762eb2fe99269b266e4be07e2ee4de7144"}, + {file = "librt-0.16.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c71d1b76210a36729fedfc5115069b50a3d8619054745f8758fe5d6f19e86671"}, + {file = "librt-0.16.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:473eebc7866bb0a0c8849a292b5e7157c1aba5d14d0f0f610c52158d6d964262"}, + {file = "librt-0.16.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4eb1313a19847089ee81e88742abedf285c60538816640b99742d8534b81d26a"}, + {file = "librt-0.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ebefd60b42e2a82b32d136bb5f7c94eadfcd29f772b547df6f3291d1ed855a1c"}, + {file = "librt-0.16.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6c5da27e8056439f927ea896735da60e616c477a8293feaa3233d4e7781a6726"}, + {file = "librt-0.16.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:67e718c7a43f8db325abbbf1404e2d535f12f8f7a1a82259568385cc5274b82a"}, + {file = "librt-0.16.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:314e703f0c19320dc8094e7a784b9cf29e1b67402515abf580069a6363c0b4f1"}, + {file = "librt-0.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:943c6bbecbdf7fa575a4f2952fcfd848c88ef95507c3fca411e89d4ac3ff8143"}, + {file = "librt-0.16.0-cp310-cp310-win32.whl", hash = "sha256:7cc365f006891afb006b52d5ee5ee74c09306ffa20e2f8705a32a4450af2f3ba"}, + {file = "librt-0.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:0314058469f4d2fd279ce7c62ac274ac82c3918ef7db62ef0697c4c359370155"}, + {file = "librt-0.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fe4372c52d4849096c6cc1cda2817d293ec51440c890474ed59ef38d46556f18"}, + {file = "librt-0.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c72c5295a84bd249526da9bdca38f2e176d15c31c13bb0063c5053f4ca023421"}, + {file = "librt-0.16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be56ba9c884143495b517f23fe794ae367d58cd89ea0fdd6d437e3c024a87f9f"}, + {file = "librt-0.16.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:ef46c1a29ffb8c72e882e22618ec618778eacd0578fb22c6e7cf9c11d15f357b"}, + {file = "librt-0.16.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d3c94211ee0c4f8d649ec06b7c115c0ec4eadb873a0e3154ca15cef3f814b071"}, + {file = "librt-0.16.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94aed6a8308818b91677957d1bd03188869cd7aeb23c5dba7912a6c0402f7602"}, + {file = "librt-0.16.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:349c0bcb87ebd07481b6ff781e25cdc699723dbe2212e57dabb27f7a13b7b87d"}, + {file = "librt-0.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:001bfd59a7d45b17e3e75f2a8c6405280b35e7b84471792778e718c4f368950e"}, + {file = "librt-0.16.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:28e038895b998d7a0c7798922ce8a1dc157675df5cf1c9ef0aca809ed804b7a1"}, + {file = "librt-0.16.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0dbe4096a7ecc00fa835d24510ad8545a4efef738dac96e0e63516783ccde905"}, + {file = "librt-0.16.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5cd5b092441053364af968ea12084692cb9d4a22f3ce9524e377880bf028761e"}, + {file = "librt-0.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ddeb3c9dedb461bb457c6c7d9aa7fbf35329da313d1a7543d00c8d0f3473c96"}, + {file = "librt-0.16.0-cp311-cp311-win32.whl", hash = "sha256:e05108e0849966f53a8d2d3112a7af881d0efaa479bc735bba91108f9f2350a7"}, + {file = "librt-0.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:5f49cff01bd608ef7d97104cb035c75455e79c2d70bf4a506cf773338ac1860d"}, + {file = "librt-0.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:1d28ae980ae2218f9c5b95d191e947296f918c9bf0b400d467a9430275bbe678"}, + {file = "librt-0.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fe52bf4641069e7978a14253b036cb9002def1926317e710f2e249f8a8c47742"}, + {file = "librt-0.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5bcc2c4726ced915b00de0c9856a4eeabfb3fddb93e10e0b8f735b7709358b6d"}, + {file = "librt-0.16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff7baa55f8e7c69851419e50a666015d02a74198716fd45c0125a2112e0a389f"}, + {file = "librt-0.16.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:b95d5d92ab83d39e760a52091bb1baba664f3a2351e39b1e16801e5747c2f0e9"}, + {file = "librt-0.16.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b6d085d70bce51d43c5c7c36d63490770180d8779e71c49305c87b4213918de7"}, + {file = "librt-0.16.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36e53948e99bbe3ffea257124cfcae1cfb01831555c9a9c903c9f9a72db7fd07"}, + {file = "librt-0.16.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:54d11f726aae9df5a6ffbbf0a03a52449bbac84a53ef03669cb41cdfd4ae41bf"}, + {file = "librt-0.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4323193ac0cd025f85af531df8ba91bf24d1973b401697347a6282e8fd3fcf5e"}, + {file = "librt-0.16.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e42f8e098b9c5396fefa05fb1cc7e33b0e08fc51da106b5de4a45fd22aac6743"}, + {file = "librt-0.16.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:39ec1d5a14e37baf1450a6cabf03fe552340808bf1ad9d71824ab90117716459"}, + {file = "librt-0.16.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d1aabe3925cbb4a08d15b7b20ba4011b53019da0c4173a25155139b7b1baed65"}, + {file = "librt-0.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:300c3ffdc459f4a779a8411ecb188e3ac0b1ff3a3a7b099642555dedae06c69b"}, + {file = "librt-0.16.0-cp312-cp312-win32.whl", hash = "sha256:c17194318e4c0c0348b36f36c2ec7534436fe0a4c15582403162a4f08c80797a"}, + {file = "librt-0.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:25a58a19ea8d83b68209f04912df765e9260635ef77646542ed4b4abe6bc7940"}, + {file = "librt-0.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7be7cf555bc30ec12622e9447299cc4a9b8ff307548b634794353db0c2065dc"}, + {file = "librt-0.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c5e6144e68b577f157519f2ba88ca20e3ed61c29b00e5cdfa76cd2d45acf059a"}, + {file = "librt-0.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:33f41443a1f4e1f099331b3d8120e409fbff84b9760bc1cc9ea496f37ddaa5cc"}, + {file = "librt-0.16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e510b7770bee609617a3374a96548eb114cae048023e3f049ee449e7ff2db32"}, + {file = "librt-0.16.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:efc49c462d4516b8a58b00b490078fa64689fd1fe66970cc190131d7afb8027e"}, + {file = "librt-0.16.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:92caf82ebef5e12d21c72242b70d1e92536f1711cf2a727a4c276de4b4469087"}, + {file = "librt-0.16.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17bac7f7a16b328fff77e440287693eb017abde913595b5827ebccbc21ecd8a6"}, + {file = "librt-0.16.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5b976054553670829985ed767feb78fb6bcede0175327c4844dd5c281c1be659"}, + {file = "librt-0.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0058f9d68721094105917254c72ac0569117bb7b13b9769cf45d26d89f9d21cd"}, + {file = "librt-0.16.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b7beaf3f4487b7d8adef1f158b49067cb4d5a19fa7a3bf31a4e7a820e435c5"}, + {file = "librt-0.16.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:468df902df016a06eb0e40b0747dc8d14e47d7a38b18b63b1fb167d85cb94d63"}, + {file = "librt-0.16.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:aea7b1f2b125dad5de85f049136651bff256c883c65e6b9209b2da0a1ac3cdef"}, + {file = "librt-0.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a8afb6557920860b7a3a596eb804cf37e09e7cf8a803db2478c202acc72d8c2e"}, + {file = "librt-0.16.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:77c7a2b4fe2c1369e0d5aa1cade26740a7b14be32fbc9a5535d617d20065c39d"}, + {file = "librt-0.16.0-cp313-cp313-win32.whl", hash = "sha256:02d89c813d5ff74b17df72d3a34819d132cd168e56b81bf755b809bd9e46b8c4"}, + {file = "librt-0.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:14ed6ebe3e4f85f326d7920011ad30ff49ed9334e62cf88caef9ba973d9e3a92"}, + {file = "librt-0.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:83d4041a3d9b2fd053a8a4e1f22878b3e5833e2712956382d5c048d791454e91"}, + {file = "librt-0.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:931a0bb0fcac88f263e269e46eb30ba8e21402cd3c62ca40cb97034c0693fab1"}, + {file = "librt-0.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1bc17e54e5305f8d40b7ca203671ff5a9e59c1d0f8ea0f625dcca53a3984de11"}, + {file = "librt-0.16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:877698bf6bca5721d8be345f2fe09778e40ecadea8b58c73075f2b1a53666bf2"}, + {file = "librt-0.16.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5981c011b306781ce561e18e14230a14524a3d8109b97553666c942c18f31a96"}, + {file = "librt-0.16.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:afced3dfc17cd805ecf7a3d77996a71cf5f2c75aa66eb0c21a9930f4fc992f86"}, + {file = "librt-0.16.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ca8052401c55d7511dda6760719fda7618067e83535d7d0010096d216c34b667"}, + {file = "librt-0.16.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1e511762a074005bb0aa569166779834e75e438370226930d0ce1866d4b6a33b"}, + {file = "librt-0.16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1e8591bd8a5a628cd7f07954c6a1592359a878bf032957a8e9057a41d644311"}, + {file = "librt-0.16.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4aaefb4ba6c07e1aeebb2795c8958148f1d6f9af3b555b53d23d766edb6d67a"}, + {file = "librt-0.16.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:d92db7a0f6aee44f1baee94750457e8d2d1c6ccea41842de6268d34e8dc7eddd"}, + {file = "librt-0.16.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:378dfaffb38e59c24a87cde5713cd865d51ff7383fa12947f3907f306ea1ca55"}, + {file = "librt-0.16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3e0c39bdc85370422e8b637be76eb1fd07d30967551b03e62267dd156f553152"}, + {file = "librt-0.16.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:1b384b90ab79a7bc30b566895809a636e0666f21f3cf12b54823d025b7e83839"}, + {file = "librt-0.16.0-cp314-cp314-win32.whl", hash = "sha256:52327da75a94012e7f932f913d20d3876bed3c102be00e6c3e8600ff7bdd58a7"}, + {file = "librt-0.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:3f0b8114c44b2ac06ff5dacd08e07e8e807ff4f46083f2a1602685122559be41"}, + {file = "librt-0.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:8caf96a4ef8fb27d0ac0d1ad8337d26a240acd4a02fe4345d0a8f264753e8f99"}, + {file = "librt-0.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:953107e2f68d0f3512c48f898b0dbf0ce5cc52bba0f318d847c985dc555ee4cc"}, + {file = "librt-0.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ad37d5b9abd49c9a655dcda7ea52a8a752884062ef1ee71ae17c2f2a0f81fe6a"}, + {file = "librt-0.16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c4aa329c17bd1aaea4f6e89335d8ccd494b3a5830b6654462273e50e11023f0"}, + {file = "librt-0.16.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0ead24d2562a49473dddd9efef8581f020007eb0054389c3ee3ffad38b1ca4c9"}, + {file = "librt-0.16.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:02118f56a9c36ddd07dfd9b919d9ecc117ba20a90987d56aa4c429fa34509188"}, + {file = "librt-0.16.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4e29522c62e28595ff7e324c6834ade51127707f0e255b18d1c1cf03d39c1048"}, + {file = "librt-0.16.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3ff4b2367926b69c6215635902cccb04048e73094e9862900d27cb2c6bbff143"}, + {file = "librt-0.16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c6f1b27bf1632a7e016af9f145f82be95e1edd7721a646505c21059257cb5a04"}, + {file = "librt-0.16.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:5696d7f52e7b37217cb3a8f92c744fe835942602fdd4c1a8bc4741d3bfdce15e"}, + {file = "librt-0.16.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:6072e92dd876ff6ceeb6cf371e35e51f479349837391341f479b08df4564242b"}, + {file = "librt-0.16.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:39ca4f2f2fe05de8e63493da592d84311adabe5bef52b193851981da9816b302"}, + {file = "librt-0.16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f9807485a908f00355820f18e91e045ffdcdc5adb68aaec40a1e2b88c5f7bba1"}, + {file = "librt-0.16.0-cp314-cp314t-win32.whl", hash = "sha256:94be5cb7bca4df6201f4183e9e4fa2086c655283d20b38cd84500a69057575a7"}, + {file = "librt-0.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d46ca272b251d033dd4527b0dec5f261a28a52bd5fa0f99c117b0a1f8588cc2d"}, + {file = "librt-0.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b9d6d4b14e92d876f8026b54c20c445f36425214c1081dc76f74e40db386b82b"}, + {file = "librt-0.16.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:6fe436af2eaf630474f491af5d032cbe45f93fcff5c3b9fe4ab194a7255b20ff"}, + {file = "librt-0.16.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:8ff5d26c529336be9bd7ae04483235d77778ee7d6444a95353102b542601ce81"}, + {file = "librt-0.16.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:909d8e3c1faee44cb762b1c519ff8613dcc5ceae5c99987a00917b5a31fd1d6a"}, + {file = "librt-0.16.0-cp315-cp315-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:6d4a64283ee61824b5790de882bc68e2d9d7a5143537cb7a966f7354f71646d4"}, + {file = "librt-0.16.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5810ba811297fdf37a1531a57667cb8ace0842013ca8606bf9eb7c24cf4be154"}, + {file = "librt-0.16.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e56aaf8c167548dc8e5d6f3bd0f48dcdd299a23c73be3f744aab79d99e9c7f5d"}, + {file = "librt-0.16.0-cp315-cp315-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f36c58e33b304b525c6c9c5076399c6ebf1109e17b9051a05a407b091b9215b"}, + {file = "librt-0.16.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:242e00b3d4fa37c3d3c1ca5f5c9adb7d909ddb1eac9c41f2787320d00caa0af2"}, + {file = "librt-0.16.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:0253721561787b8df8443eb347b7a6461015354e5bdd37ee38a41fef220d2bb0"}, + {file = "librt-0.16.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:3e483a8d69ede8067db70c0e83007423b6925de6fd53afed01d66160f2e9398c"}, + {file = "librt-0.16.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:69ba927445cfaaffb4081003ef5224c55a5c2ab67ef956f416ef744916e44121"}, + {file = "librt-0.16.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:d6a365f2ab45a984d0e00eee0dd17f599ceab8cadab6ea07b6111c8132fc0e42"}, + {file = "librt-0.16.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:f01f3805f2dae4781c0c34b440e31740d082950bdaf89a6f601ad589a28af57a"}, + {file = "librt-0.16.0-cp315-cp315-win32.whl", hash = "sha256:b0e3e721c75d2e79a76d4422c79d7ba705fe1bbafec907037fe7a657a480a0e3"}, + {file = "librt-0.16.0-cp315-cp315-win_amd64.whl", hash = "sha256:bc02954b1295de798bbdb0b4e2d8a28c2117de8b5c73dcbeb27dc32572dfb971"}, + {file = "librt-0.16.0-cp315-cp315-win_arm64.whl", hash = "sha256:c5db585d43449a5f54303d4b2774e45e1babd975cfe1630a3d708c0b80c3e560"}, + {file = "librt-0.16.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:f06c689cb14afd9b612727553a5ec5a40febf113ca41c4413a2b0b334285884b"}, + {file = "librt-0.16.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:13b4e8aba90b0b1c82474e9844aa9ffe7ad3faa484350e1da64cb8188d903134"}, + {file = "librt-0.16.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a269c46ae327d8e6f8c1f85f7516cb52c0fa48127565a1105a4f4a05ff2a0b4"}, + {file = "librt-0.16.0-cp315-cp315t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a33e0dae1f8592146a4764d54ce842b278732d21a84e17c3bbe6b1bc158a2248"}, + {file = "librt-0.16.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:47ada6ea32636492c61aa8ad27ae3b9404bfe7a97e3ba946d1984236cc741da0"}, + {file = "librt-0.16.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43bd6e642d8a248c114327f98dd25ac5a7cb5aa168ef02f0559b91874df16b8"}, + {file = "librt-0.16.0-cp315-cp315t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c3d1bb7841a816ace6449bb26d3f9560dbfa20e71c568d23f0f62bf1e68f50b1"}, + {file = "librt-0.16.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:3931f7a3db322e7f44e02a280e3949326ce9579ad388ee8d691dc7c76da9fb70"}, + {file = "librt-0.16.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:f4462528b6000afe8f16907b5c7c2553abf1df005ba5140e6eb394541c3624c3"}, + {file = "librt-0.16.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:80039ba9b6a7d5f1a0175a4cca6bbefead87bd854c80abad1cb30afe47a830db"}, + {file = "librt-0.16.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:7a1d272724b581bb6bc769dfdafed6da2ecc9886ba2450311de55a4ac2e1e9cd"}, + {file = "librt-0.16.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:fbe4fb8c5445f7496d7f7f6bb0807875d09d47e6771ffa175fb2df2895fb86ba"}, + {file = "librt-0.16.0-cp315-cp315t-win32.whl", hash = "sha256:375bfe6b572a8f6cfc398709356046173bf27e64c4c5edaf5f7062f051fb4bf9"}, + {file = "librt-0.16.0-cp315-cp315t-win_amd64.whl", hash = "sha256:bd3150023d3dc2bc70f3784e59ffa1140d56ddba3d8125b3d6f9f85221279bfc"}, + {file = "librt-0.16.0-cp315-cp315t-win_arm64.whl", hash = "sha256:8ceafb70f2a4f0826f11031942e59c0728fd98da112dc346d4352bde1e486866"}, + {file = "librt-0.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e1967e36ac4cae0c7e9615ad32e1a513cdacff79f9e8afb28bedc91caf48b4f3"}, + {file = "librt-0.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:20fe0bf9053885e21c62b3e091fb5e73e1c54d1daf2e70eb388d70763bcd4220"}, + {file = "librt-0.16.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a63610fa76524edfa605b5b259a603915c7a6e10e54f003e5503030506e81de"}, + {file = "librt-0.16.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:845a511b60ca43b9880dcc84a9784c891d6a2098c829130b320846c69c9c0c68"}, + {file = "librt-0.16.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d608f0bf3b8cbddd0067fe02cb8cab7d13e8eb9386b23a1843d4363044fd9e22"}, + {file = "librt-0.16.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df183721229ae51eef90108c115b43e98cb169b6155d34480338e5fc6616df00"}, + {file = "librt-0.16.0-cp39-cp39-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4b6183e2e2e0ee00aac2ec07c7f7d151c97e85666b304f574b71cff0f9fccc4e"}, + {file = "librt-0.16.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:273d00be33792a15189331df10f1f1331621b043881e66c6c4377f7f776e1291"}, + {file = "librt-0.16.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aa9357a1b4d4fc787bb718a59cb1112c28c8a976d6bfa268b71cc0a4ab8f3a94"}, + {file = "librt-0.16.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:e9ce0bc440e7fd09b5f51f372f0f6640658f854b8fb05260f819cc669c93c42d"}, + {file = "librt-0.16.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:6c8893eae2fd13c5488d94056f3e6e5cf3142bfb1c4acaf136cb33d760c5964b"}, + {file = "librt-0.16.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7393c9a48dcce4817dbd4b0d8ff6237efe9b0a0609f5b0adaef315f8541726b5"}, + {file = "librt-0.16.0-cp39-cp39-win32.whl", hash = "sha256:71b93b42784e25b975079573c642a8fedb049a7bb31d70a51721b1666b3b2ced"}, + {file = "librt-0.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:5750a105b42a416f930edc59054927a406effb2550cd5bab92ad7a5842ed5d05"}, + {file = "librt-0.16.0.tar.gz", hash = "sha256:ac38d6d8d66bf3d744148dbbc0b8e193e195a51e364ed55e224631f5721891fc"}, +] + +[[package]] +name = "mypy" +version = "2.4.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "mypy-2.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffda5244fd1ad71a1e54405e35f50d09b80c3978efa120f58bd1252ae32c62d2"}, + {file = "mypy-2.4.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:615b03922d40e186fd1df73156473db0ba525c639bb4fd1cf28e1694879c9b23"}, + {file = "mypy-2.4.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13fa24f439c0e48a290a3922fa14ccd22f2762bae99d2142931b3e40a9055080"}, + {file = "mypy-2.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3adef556a19eb3b630bf86a79c29d7da3d61e541472d0d897ca01b171c0abf8f"}, + {file = "mypy-2.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:1dc0f64b0a92ae27a49d2175f0bacfa56e15bdc5b420cf92e0c1f79292219cbb"}, + {file = "mypy-2.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a378fc15fb33e321f04652c166ce73eeb8833a97c3d218132844e938cd93220"}, + {file = "mypy-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5d20e6c7c35fcbf2a0ebdd0eaeacfbc243009dfd33ab7822d54e213912e6dbbd"}, + {file = "mypy-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3011537be6cf1de4511c0255a324362a812b58184bbe61e15f59c8b31033bd74"}, + {file = "mypy-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a96b07a49b7b1d025ce59c1b3acbcf24bead9a83da4523c4a6bde1bb94e7a0e1"}, + {file = "mypy-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7c4f8f8d1d1c0e2832d8ee7113dd08f6df6c7aad9e863fcbed9f25832be0b8c4"}, + {file = "mypy-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba05652540bf12828e52abae807b024b09ca144ff4f75e2450a81d69c376425b"}, + {file = "mypy-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:6306086b87cf7f8a29aa618d9fd9bffb56c59247166b9660fdb54d86d7714ecd"}, + {file = "mypy-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e05ff2925d8b37ad26c80c1b9dc43ae5d455da2df1e23c24c095a6425917c57e"}, + {file = "mypy-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29243242cf72582b65f9582ad9e56e8cb281566ed3519f4cd70bb8b9f2977e90"}, + {file = "mypy-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29eb0b9427a6b11b992e452f6cceb8af724f4dceb47e779d0b35e405e996ea5e"}, + {file = "mypy-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e3ebe2f72a2a1156065a9851570ffbf50c0a93cdccadef9c6e05c508a4fd10b1"}, + {file = "mypy-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:236e0d68f6941992b0811128e652590f590db444ab29ad8f1324765b9298b946"}, + {file = "mypy-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:82d0f94c8587ccb472622ee7795280aaa38a06640d5f45b3f16909d6dd86a989"}, + {file = "mypy-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7da85fbcff6dac1abcc636707bed38b45598131fb7a605d9719c70b5cc733af8"}, + {file = "mypy-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4209da39d85cf240f762af622d8180fcdfcb4727d021f44ade62d613a1a43324"}, + {file = "mypy-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be721bd4bd57576193653b75b4af3461c9d0bf7dd8b528f782e9be210dc75bb"}, + {file = "mypy-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1fde197ae65be856a034a91b70ed747a16562ca69577785f06c661548424bf1"}, + {file = "mypy-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:295ecf2e57542cd836ca537486951289678c8c7d1ee6ad74ebe29b2168a003cf"}, + {file = "mypy-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:bc378bdad4e9f12b5bd96466083d1e71acf00594ec9c7b2bdb5e02816f77f303"}, + {file = "mypy-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:058165f564ccf559c68c70fec2091fca5891110480210c22594635e3f6683437"}, + {file = "mypy-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9fa247e02b505a45a2775f69df38d360d197e3790bc60f717595db9eda358b6e"}, + {file = "mypy-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20e9a5cd875837520c43db98dea0b6d0c2197833d95c30127d8f570fb9b1f00b"}, + {file = "mypy-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9f03a7828cca2b0adcd6662aee8f2711ff8830e1027641fdea3ab0b787483566"}, + {file = "mypy-2.4.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:9279488933040b638c0ab739084c0ca100efeea6db581bf5d7628d8e89de53fe"}, + {file = "mypy-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:2106b55105ba5ea9be4f53a24517fc5fa927ff1585edc9bc1a975abb72caef89"}, + {file = "mypy-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:528c8744b8b5e3ecb8774f86af38d2376216816e9908317ad055f3c9c2d74799"}, + {file = "mypy-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0bb95cf34899e4619c61ab0a8667804e139e580b30d5df12af2102dfe44d0c97"}, + {file = "mypy-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86d616fe84c6eab8026f8c50ab5bcb90db780d2ccd233d971e34e92bede9b359"}, + {file = "mypy-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3f86fd1313dd69d013e265f1fdcd12ea7a9d606f9875b2a3db946cd334555f3"}, + {file = "mypy-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:720434d48542ecfe84d32d287b727569d3fc8f5769acd39051130e490a5c295c"}, + {file = "mypy-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:a6e851b82c0661f69f1630fc16172c68787a6a9cf0991e7c6437d60976cdcd76"}, + {file = "mypy-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:3bd0e340f0ebe65c548210f53be3fd8192e83964760caf0c28bef368e68b0d37"}, + {file = "mypy-2.4.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:afa89837d9be67e0cadfa33bca3bb7efdda98c3b07e74dc3b635ebfb1c8a926a"}, + {file = "mypy-2.4.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb443e81057896132d3642d6be219e6efd158691ac7883e3ba8fcb469865f05d"}, + {file = "mypy-2.4.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7f38f57d344f8b6accb40e01c3d83cfc590498231724d16c07ffb7940f157818"}, + {file = "mypy-2.4.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e76172710bd4e5eeae061abfd68347e5264632e02778be61784671ae3a2132f5"}, + {file = "mypy-2.4.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:f83353e47ab520bf6fd4df8f5897d9fe081211f2fbc4b7d37736a3e3c166cbcf"}, + {file = "mypy-2.4.0-cp315-cp315-win_amd64.whl", hash = "sha256:970b221ed5842213d98e3c480c08f795ace4b1f81fb21e1b126bd0476bce1c34"}, + {file = "mypy-2.4.0-cp315-cp315-win_arm64.whl", hash = "sha256:502b94b0b331f7dafe32fd6b151797ddbb4f32385b362e722c783a025e5954a3"}, + {file = "mypy-2.4.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:c9de622fd397495695d0598ddc789222bfcfec9d7c9ec3a1e385c855e3bc5e01"}, + {file = "mypy-2.4.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f459f0b4f0596d9d51fe7716b404b35287b99e77da98a7af90a65dd5fd61141"}, + {file = "mypy-2.4.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9b028548b3af480e2b1ed8df14ccaac86f99c9f600d1580770ab7ba3dcd40f0"}, + {file = "mypy-2.4.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:cb734b2668c1f40d07ce093bbeb4407e9527c67901627b0e1679825be3f09975"}, + {file = "mypy-2.4.0-cp315-cp315t-win_amd64.whl", hash = "sha256:172e30b8fea631fe310f0c665477f52d9ea40bb4e99e0c81dc30118563b13710"}, + {file = "mypy-2.4.0-cp315-cp315t-win_arm64.whl", hash = "sha256:5786ef987b3767e51aaa53f20aec104c0252b42ecda7aef8e8b4cbae279b05c5"}, + {file = "mypy-2.4.0-py3-none-any.whl", hash = "sha256:d01c5d26a352acc6d5cf3128225477e1e8465e8d3029d4c345807fbf7f3cf093"}, + {file = "mypy-2.4.0.tar.gz", hash = "sha256:77bdaebd452f43fcfc4cc3ba94352a3ea537cd01e3f2d0879f48673d2ec00d6e"}, +] + +[package.dependencies] +ast-serialize = ">=0.11.2,<1.0.0" +librt = {version = ">=0.16.0", markers = "platform_python_implementation != \"PyPy\""} +mypy_extensions = ">=1.0.0" +pathspec = ">=1.0.0" +typing_extensions = [ + {version = ">=4.6.0", markers = "python_version < \"3.15\""}, + {version = ">=4.14.0", markers = "python_version >= \"3.15\""}, +] + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "packaging" +version = "26.3" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c"}, + {file = "packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79"}, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189"}, + {file = "pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a"}, +] + +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "pydantic" +version = "2.14.1" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "pydantic-2.14.1-py3-none-any.whl", hash = "sha256:9195d967ec791692a04438115466764fb8b9a27b31f14a760437694f40d6b454"}, + {file = "pydantic-2.14.1.tar.gz", hash = "sha256:94f478203dd03404682a1ada216965651dd74b1d2d5ffd62e00e0837caab5c26"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.50.1" +typing-extensions = ">=4.16.0" +typing-inspection = ">=0.4.4" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata ; platform_system == \"Windows\""] + +[[package]] +name = "pydantic-core" +version = "2.50.1" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "pydantic_core-2.50.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b281a3b0f0822618fe5e3e0d8a2048b6356b14388505dc9374ccffeb69989713"}, + {file = "pydantic_core-2.50.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fa4c8bc12c1354c5550c0c35c1852c8c1901e89e06561724e03f8d0342e1f87"}, + {file = "pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa9de446b793de2beb6fa2d9d0961803126c4e2a99c2f25ab59b9fd6ea125c0"}, + {file = "pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de531ce1e2a3364e8767878b58f4ff728a434b4fde089781fe30b1e08e2396e0"}, + {file = "pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7c58106de36ac6a56314182958de20db8d3a29dfd5db527192cc754e4f8e7fb"}, + {file = "pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b4c3df25bd323bf1d36a648d563cf1fc69d717451569927151bdad7cad07a77"}, + {file = "pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f77ac30b19221cd9bd3fcfa3d4614eff93140d0572ab730cded17b64adca05f3"}, + {file = "pydantic_core-2.50.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:d939de9c82e2126f7f48a7e658f8a85ed46d57662d53f44c49b8895fe94a3eb7"}, + {file = "pydantic_core-2.50.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:30ddf019d082c117b5d309e5b86710c2a78909907ec1a9381feec3eec02eca0b"}, + {file = "pydantic_core-2.50.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2ab756b72bd5054e4c7ef3ded331b35786cbd3cf931531a508f79a9537517064"}, + {file = "pydantic_core-2.50.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:b087b1c5be7ac687cf22eabfe4b6b608d40df23610651e93611e1f49118baf84"}, + {file = "pydantic_core-2.50.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a44101320cfe99432db74237545a63057dc7a88dfe792cbcad0647f2af56cb81"}, + {file = "pydantic_core-2.50.1-cp310-cp310-win32.whl", hash = "sha256:a4aaaa791bdae1c972a7e81765f4f3571c926b8e0b9b6e47346499fb80079665"}, + {file = "pydantic_core-2.50.1-cp310-cp310-win_amd64.whl", hash = "sha256:2eedf82ee4753cdab8e50044c6bd569577eebc3859b11fecf4eb9223761ff966"}, + {file = "pydantic_core-2.50.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c531166c42ea7bdfecc8c50049581f05dd1993b09cc7c52bb36a14e96deaec7d"}, + {file = "pydantic_core-2.50.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6d0c2183008c188e19f4906d426b293bdc4f67ab17df8e180fe16cda208fa71"}, + {file = "pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94be440c03fede26969a5ce75468e0e6a9927a1b46d9b679ee8adc1b057b0350"}, + {file = "pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:36c426eac0af8d1529ff8467e612b933346caec1fdc0d774f78f67a1a11e16c1"}, + {file = "pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:028e2f212273d4a39b1ec1e0de8166b1165a65fc0f1111452a9d94fc7c625c63"}, + {file = "pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6f0cc1bb9900dc558960894adeb30b0c083366fc1d69b856209fb2ca5c36fe5"}, + {file = "pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8812592c85d0edf423f10eadcef42716d71e8219085ad9e85b775057b7306133"}, + {file = "pydantic_core-2.50.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:bbce99252ba3167b2b6277f1829d5bf4b43b754524bddf7f944707c3db7d2253"}, + {file = "pydantic_core-2.50.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:476f6ed8e43cd1e0b460920e23571700872b284e77331cb30c4faf459cf48a4b"}, + {file = "pydantic_core-2.50.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2cbd1b75b09e976ed0d6b6ca297675632ca35df86130088457cdc60ef36970ae"}, + {file = "pydantic_core-2.50.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:5958c72adb417c39b12ac87525ac60b0d73315fcdc59e21f44ee4a5e2512c9ef"}, + {file = "pydantic_core-2.50.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d8f9e8a6c4ab04b78d61f78627370d834eb004b2869dcb28cfffa647b4ea1980"}, + {file = "pydantic_core-2.50.1-cp311-cp311-win32.whl", hash = "sha256:4be846f55c9477f5f3ddde8f2ce941137e16862a56d018ed885d422bb6ae02f2"}, + {file = "pydantic_core-2.50.1-cp311-cp311-win_amd64.whl", hash = "sha256:0048b6dddc8ef4b64fccaad878bd143b0c3882ea9936279dc11d613f6b7dd1bc"}, + {file = "pydantic_core-2.50.1-cp311-cp311-win_arm64.whl", hash = "sha256:6a733778df2f7087ec1100ed0b41533e4f3001976e99570fa34f57c66e7f8e3e"}, + {file = "pydantic_core-2.50.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:704075d10b74f2f3c6e15407c696d88701df35fc8953f434a431add0d0074db0"}, + {file = "pydantic_core-2.50.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e8e1d6ce820aa23317e8209a86bd65a540973c12dc7552b48a4f6c8e9926815e"}, + {file = "pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c18db21573bd2c6489f9a544b7499f0df2853958c568e5e783536ee1f690af41"}, + {file = "pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb57f304525a5e3c13333b772bf9a473f36326e9c821b2e8e1b2fd36f80ae2c3"}, + {file = "pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a27c09d86600f1bf2fe3f37e1ae697faf3143931c09322cd799da94deee923b5"}, + {file = "pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46b3301d3b5c886f77de7546e47274a5842c622ea2020b8c6524c6b66913b4a6"}, + {file = "pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ba4e9d8210d941c200431a56b2c0400b131865947903937ed3ec5404307d2e"}, + {file = "pydantic_core-2.50.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:e5faeaee74a57d32b3ab3aebad2e348f06d3ba946fc5d28c1728455f00a3d13a"}, + {file = "pydantic_core-2.50.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3cda0e538208e5d722bbf3698b24f19c0a7d05bc8d5f8a7f9b121ea7fa243d9"}, + {file = "pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:57f51b31ff826e2859120cf4737c5a758a48d96f3e97da40ccee1796d58078ff"}, + {file = "pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8daa7ee75245d43ad7d747e5c9ecc1b1d06552f72b14887e9276f787d57375f4"}, + {file = "pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:acbf31f37c53a5ac0c34706c80b4f5107ba20b05fdd3816124bf236ef0c57dd2"}, + {file = "pydantic_core-2.50.1-cp312-cp312-win32.whl", hash = "sha256:45b11cac094aa25725581d9304eee93c9028516b9ea80dd9e175e13a5a2c840e"}, + {file = "pydantic_core-2.50.1-cp312-cp312-win_amd64.whl", hash = "sha256:132529c83901437ff642f585216831bf5fd7a91df66829907e155192ead62498"}, + {file = "pydantic_core-2.50.1-cp312-cp312-win_arm64.whl", hash = "sha256:4e834f6a8e4ff772dcc34f58ef5504147a3ea5b0f4eeb13b0f8eb2ca75ac57f1"}, + {file = "pydantic_core-2.50.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d5e062c01286d861fd6a1c4ff6e063547b3e713067f2df033c0ff97ac2ca006b"}, + {file = "pydantic_core-2.50.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0c003c3b7f49debb893d2d85ae099ac5959c9839e2f330fadb1fcdf7a6594482"}, + {file = "pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:409e0ea40ec30d9158f33574fd758e689f6045a0f2596701828c27816ca9687d"}, + {file = "pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:131059670f1d2444269b8585cb888963994871932447c08b39ac6a51fcfef658"}, + {file = "pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dbcbee53bf17196a7f745aa9bf5a9603953a1e365b1f020be3207c676a3e7c4"}, + {file = "pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:325c23f3e35cfbf0fe3486fa5f7260d1e45885173002d30a28ca019994124255"}, + {file = "pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17e722e156d0444ecaefbe640bdb60928752bf2013e2b7a11cdb099aaae19bec"}, + {file = "pydantic_core-2.50.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:aa8224f10880d9bf1b5993988ba153d42a8b4f3f4f511f93b1f09c93ff613c72"}, + {file = "pydantic_core-2.50.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:41bc8237121bd8dc8d888dfd6279fc166ffc88c1f1bf3a8bf00869680533ca4c"}, + {file = "pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:45c6266d071c241f2a168d45bf8c54344f0effce35e7e6b73afdec11f3687568"}, + {file = "pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:1deeacb112d14d3f4fcb16b165f7dbaf76c70ba6e82f37ba042bdab51970a0b8"}, + {file = "pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1c96fd793b73d1b92e65570132505498fe7b21eaef73cdf74e67e5dfba7ac9e4"}, + {file = "pydantic_core-2.50.1-cp313-cp313-win32.whl", hash = "sha256:06ead20d39ffd6f2f6f2a8f8a6de67ff8bb1b4f14a8a30e058502514ee2ac685"}, + {file = "pydantic_core-2.50.1-cp313-cp313-win_amd64.whl", hash = "sha256:7816e98acc08119dc0f340ab167048ecc54126316330c1f0caf7c6756c88e28f"}, + {file = "pydantic_core-2.50.1-cp313-cp313-win_arm64.whl", hash = "sha256:c17799a62c142d61b8a3c51752a7cbc87fe2ad4ccfab10e628a77b405075c662"}, + {file = "pydantic_core-2.50.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1cf41f1ae3fa155cf167a72689ad044bcc1e3c97e064123677149bdfb5dafc4a"}, + {file = "pydantic_core-2.50.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4df197990c15b5a37c5a277d131d9f2c67de6133f2e5dafd80d9bba4b99f46f9"}, + {file = "pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0036473f5583e6a60e50b8b21651511564277a3f05cc5dab8cf579f552cd5f6c"}, + {file = "pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:992c3514ec891fa7858099183e4d64e6bd5a5d4ff452fae29df22faa77a006bb"}, + {file = "pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:739dc730e6be3bd5ec2f4ab5cfc7eb047cc45fc1497b3bafec74ff2ed07df597"}, + {file = "pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32fad3a91e51b6d2039c572db04a5a873260b399f6bd62c3552671fa7a4a2899"}, + {file = "pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42b54c2c90ad348b5e3a85e03e715d572c1fde357ef104cdfe3b03b697a404ea"}, + {file = "pydantic_core-2.50.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:2df1ff41884de2bc4b307bafd7c40a691094fad2ff8e767e5b45a319257bcf4e"}, + {file = "pydantic_core-2.50.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe90228920fd8ff2be62622b6bb8a2b11acd65046d50c6b130614b5879605a20"}, + {file = "pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:844b869f118e22a41a091bdcedda8a71bc1b0f62c38d1a0c3211cece47e1d8fc"}, + {file = "pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:2eb75304506894a281d346220a4f7481a1b8729577c5ed2a05395991966a8396"}, + {file = "pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6b20a4bffabdad0db2927ac034ae3b8a681b1f7a0182f3e60b479ad2fde21ebb"}, + {file = "pydantic_core-2.50.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:99ba9bc2b8062ea0c326a990f7f00e6530c23579de66dd246e72c4cafef950a5"}, + {file = "pydantic_core-2.50.1-cp314-cp314-win32.whl", hash = "sha256:cf356f70551d40374eaffb1aa63f1eb6d2006681cbd7a9faea173ce0f4dd7cd2"}, + {file = "pydantic_core-2.50.1-cp314-cp314-win_amd64.whl", hash = "sha256:d32f3acc081cc3923386d88f422cde8892335e95f034e0104bb4cf9310d9915f"}, + {file = "pydantic_core-2.50.1-cp314-cp314-win_arm64.whl", hash = "sha256:bed5163e03b98bc1fa2eb05d74c63d9c5c95d8ed6254985481640fbf5e237dea"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:9572c1369e9c9da2d64a7b7992c786d90ff295abc93964cfe3125e4290768070"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2005207aafe1231315718bf6ed5d064a7300fb4772754af35ee72fc68159492e"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f6047f62a6c5ae08d0a6afb035667aa2d97c3d20d69762e034c5ea144d92a5"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ef800dd7d85bcdadf4c3076e4c94e43939493558a3b69a1ea830c706d4617bb"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b0135bcdcaa0f23573f286e4cb5e0fd2962700964ed13df085b85f2b97aeab9e"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b3a6f334c6a2345ca15318ff894502a90012536404b37c844a976c76c846e0b"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06e01fbbfdb9be777b316a71b6c49efaf4a08b615d0a98d678cda3023f79d019"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a29a061fec0b4e2d714f277e70a3a18125ecff803f2fea6eade2f2e53711d112"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f5187624823423e1d1b82b1072ac41dc837389e18d3d0572cc19bbee46cd550a"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:3e46a9eb0a0901dd6275e6b06ac3a464885ef350ec4121fe486869de8053e4bb"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:756d669f04e62ec4148ecfe22be6a4484d9b1181a6ef32e205ebfd200540858b"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:c516cc5367ca3448995d42cb994bf3f4c9002d2a7c22eac9622551269ad1b807"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-win32.whl", hash = "sha256:9d1bed94af6a63835461f3cf7502058eb166c58c4778e11d0f433cfb1bd69e19"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c8dce1f1e0e5358b682a6ad3fa5e31b31d4560997b8e61417e9217c8d60f8a0c"}, + {file = "pydantic_core-2.50.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ceff0acc940be2715bd6ad17b24c0e5304abf44f6efd0f81ee8499e640f9dc86"}, + {file = "pydantic_core-2.50.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:8a6791afa2245e6c6b180122d105941644f5bd410bb18623b408808cc41a3102"}, + {file = "pydantic_core-2.50.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:84f34323a61a365b4e9295de6028474754829aaddd59c7bf1a040e7487ef8f3c"}, + {file = "pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23edad659e8dbd8ca7e4e877fe6c81573abbdf215bd25a68b53e1272f58b80c7"}, + {file = "pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a5fce22f1e87d181e924e12da7d81cfe031fb3881a5ddf26ad28f141756ca43"}, + {file = "pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c73622ef819328873b53109ee4f77ceb598bffedd02daf916102be3228866b78"}, + {file = "pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce8c25ca38cc0e3d7753ba180808de2c0c8cb24eae0df64491e40921454e9831"}, + {file = "pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7689580e72a642ab5ec64d5f55b2e33636fa43b4ebe63c0c2c965ef307c7d1aa"}, + {file = "pydantic_core-2.50.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:d5c0e32fdbce7f1e8ef4d11f655694bf5f4175c757a9f1dc2be09b8864e5bcf5"}, + {file = "pydantic_core-2.50.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40f523349960fa30f3ea51404308ff50f9997a90df639590f47a057c1f32b415"}, + {file = "pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:d4193206b6587047437f6f11d7e776df23e1c1e23af2a54d9347275614791e10"}, + {file = "pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_armv7l.whl", hash = "sha256:84bc765b282a9d5b7fe0348b8648904f25a6a04b2139da52b1dd30c8ac3a2c8f"}, + {file = "pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:ed1e728b39a383c81035b2459cfcb35d99dfb01f7d6ebe3a913bc1cc5b81e459"}, + {file = "pydantic_core-2.50.1-cp315-cp315-win32.whl", hash = "sha256:bc94f474417604bd383d2cd445d071b07dd55fedceed3ce33407bf1fcc107290"}, + {file = "pydantic_core-2.50.1-cp315-cp315-win_amd64.whl", hash = "sha256:983a662de2571cb2502fc8ff47b6770b03d025d2eb314c92f77b3f07c74720ed"}, + {file = "pydantic_core-2.50.1-cp315-cp315-win_arm64.whl", hash = "sha256:94845ff54dc5193f228cab81b2662a04bfbb892e95bdc15edf7399000ce57d54"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:4a53d13cdfbedbfa87f08b83c1a0a5efcc767d785a4b41934fa9cb672670493a"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:efbecf43d321f7b9281441f1f213f7c21c66988b0e06c2730ba13ed47a46bb08"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc1f08f68dac9f9e83845a8039880aba2ab553eb9b2259c3243a313182c253fe"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5dfe41f232befddb9c4377f6cfc702b51595e2d78ed082672adf8758d2c4619f"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adc06d218a1cadfd2ec4628424d7d79ce4eba69c2965e7e7b55106f0da5208c8"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2cf91809d0721ab81592ba67bea7694821679c10b1a2e3c3460082b286c1918a"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23923ab9292c40da026330b1ecf4dc2618c8e86e0422e5d1fbf50d94d64ca4f8"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:f3377c8c2b3ce898423c5e5dd94c7982e30aa7717a7e6ab2470b9de364963709"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:455a773617b5913bf5c20d0692e5787b119e52c4d40ea644ca31f5758fd31be2"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:1a9006395dece0e32e704c315eff8a00bede494f6108546cfc5539c89fef4f9a"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_armv7l.whl", hash = "sha256:d2d82aa62521c55ddfb000ae70f88cdd8de974078f6024e821dfe5addd0c818f"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:009634b83993777ddcd69cad0ffcace43dabde692109528e35f0fde91e386a8b"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-win32.whl", hash = "sha256:3fde4fdc6487a58d944ca87cf5adc95d5f266e872c19599f5f4c0a8a1b1f9f9f"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-win_amd64.whl", hash = "sha256:1c8632d4ac04e6f91128fca584b3a8a507d81604c24eeaaad00d4be42765c32b"}, + {file = "pydantic_core-2.50.1-cp315-cp315t-win_arm64.whl", hash = "sha256:c3ede305158e75510be50869b319550ab072008c13d64d4ab1e094fb286b6f44"}, + {file = "pydantic_core-2.50.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:062e891facce5ca296a1c37098e5e466780457f86413894b399f0cf22934f769"}, + {file = "pydantic_core-2.50.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:49c2cbb2397fe4d0987e84606e691af6cb87bc0ee1bd3e7b737f7e10b4c142f9"}, + {file = "pydantic_core-2.50.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e4472072de0137ee0d8e72d6620e85939c271d2f90f6bbb4b15c24638b79f92"}, + {file = "pydantic_core-2.50.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ed4f3cef55164b026fefb41341b7754cc6b624c75dfe7142d2ecceb5ad21c87"}, + {file = "pydantic_core-2.50.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:76e2e83fa6ec8cdc972d438dafc2522b3a47bee4ec0ae668b29cfb1977ab5242"}, + {file = "pydantic_core-2.50.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:a51eee75939cf811ac09b278745a6cee7dc873ccfbc8b9af3cc88fe4b7ce25b5"}, + {file = "pydantic_core-2.50.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae28183297fb0d2b8dc46a1f01d51f5e45825fc5afe76a835a6cb7fb34821295"}, + {file = "pydantic_core-2.50.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88e492e8b9d0312e7dc13667c30222abf284dc3b79b5302b3607b41a5784ce61"}, + {file = "pydantic_core-2.50.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7456d699b13954e9c0164dcb267250a10ae0dfb03e6e26d6796ab0d46e189c84"}, + {file = "pydantic_core-2.50.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0d955195bbbe489ad343fcc956eacea9357b79cb22192c66cacdefcbc14b32f"}, + {file = "pydantic_core-2.50.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2c634642694e6a0dad2ab1d375589fa671fd442edd5caf7d9737b8f6ca22906"}, + {file = "pydantic_core-2.50.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ee6db2fbed51a7991302e8fac498cd67e336246026d0dfa84cf5166ce1412760"}, + {file = "pydantic_core-2.50.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:79490e33c4c0fcb933bbbcfc3a62184d8803b99f535863dfbb925e1bcb6945ad"}, + {file = "pydantic_core-2.50.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:48569b0ade9edfbe065cad1d700175546592aebbb42f02adcebcc26e75b896fe"}, + {file = "pydantic_core-2.50.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5f3cae32fc46121f787cb2486de9cf95a8bf72aec5cc78f64c606fa1735a6ef5"}, + {file = "pydantic_core-2.50.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7f476456ac2bb0d937f75191494a09c83a30765fea4f70f3b404942fe25f6cdf"}, + {file = "pydantic_core-2.50.1.tar.gz", hash = "sha256:e50d7b94baac6c7d09927fa5ca5800a0c7ee5015c7fcff65beb3a1931b5a6e09"}, +] + +[package.dependencies] +typing-extensions = ">=4.16.0" + +[[package]] +name = "pydantic-settings" +version = "2.15.0" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "pydantic_settings-2.15.0-py3-none-any.whl", hash = "sha256:0ba092c291c94baceb5eff768aa0d56400a457585bc0175925a5a5510303da42"}, + {file = "pydantic_settings-2.15.0.tar.gz", hash = "sha256:694b793e84f766ba76a90ebdefc01d0a9a045dab0382bee70393da93712ad117"}, +] + +[package.dependencies] +pydantic = ">=2.7.0" +python-dotenv = ">=0.21.0" +typing-inspection = ">=0.4.0" + +[package.extras] +aws-secrets-manager = ["boto3 (>=1.35.0)"] +azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0)"] +gcp-secret-manager = ["google-cloud-secret-manager (>=2.23.1)"] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + +[[package]] +name = "pydantic-xml" +version = "2.21.1" +description = "pydantic xml extension" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pydantic_xml-2.21.1-py3-none-any.whl", hash = "sha256:c0469622c344485c328213fa18ff49542b814a9770afcfc6810e9771401d9236"}, + {file = "pydantic_xml-2.21.1.tar.gz", hash = "sha256:43488d244375d9fbaa593d8c0435f8b0a940d7e594fded647383ff223af2baac"}, +] + +[package.dependencies] +pydantic = ">=2.6.0,<2.10.0b1 || >2.10.0b1" +pydantic-core = ">=2.15.0" + +[package.extras] +docs = ["Sphinx (>=5.3.0,<6.0.0)", "furo (>=2022.12.7,<2023.0.0)", "sphinx-copybutton (>=0.5.1,<0.6.0)", "sphinx_design (>=0.3.0,<0.4.0)", "toml (>=0.10.2,<0.11.0)"] +lxml = ["lxml (>=4.9.0)"] + +[[package]] +name = "pygments" +version = "2.21.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9"}, + {file = "pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytest" +version = "9.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c"}, + {file = "pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1.0.1" +packaging = ">=22" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "1.4.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1"}, + {file = "pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42"}, +] + +[package.dependencies] +pytest = ">=8.4,<10" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)", "sphinx-tabs (>=3.5)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "python-dotenv" +version = "1.2.4" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "python_dotenv-1.2.4-py3-none-any.whl", hash = "sha256:42269a8a5b3fd54ffa6f3d84b18abed50064717576b4ecf03dc4a55d8aa04fdc"}, + {file = "python_dotenv-1.2.4.tar.gz", hash = "sha256:f0d53e69935a851c0dcc78f3ab7aaccd8cabef0b92382b576b824212902873c0"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.32" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23"}, + {file = "python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e"}, +] + +[[package]] +name = "ruff" +version = "0.16.10" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.16.10-py3-none-linux_armv6l.whl", hash = "sha256:488b0fe3f3574210e5cf80d9f59b9e3ab17a127a8155de3f307b392589cfb511"}, + {file = "ruff-0.16.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e748ff95c934c4e978783b8e687bc174e7bd84e8ad24e3243e1ecfcda5e0282d"}, + {file = "ruff-0.16.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3031a4a2e8e7b8a46f70be45f198c35a11ece509a94b80334d8d397a33c67550"}, + {file = "ruff-0.16.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:494401c86df4c4c25f69b9419605d944467ee98c42fb6ad405ef4fa40b8fb67d"}, + {file = "ruff-0.16.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d203abc0ff2b773ee33d00ab8df0bb67046fbc7c07b119332f08b9b345cf8221"}, + {file = "ruff-0.16.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd83d1235a5258d318477bc5b576303974cbdef5df0c01a1bff14efcc23bd12a"}, + {file = "ruff-0.16.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc2610fb269fa56dd8a68669ae470fa6272902668c0fc2ebc3aa112b2633d5b8"}, + {file = "ruff-0.16.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e70175e29cc94c26ea296c80e470180b744b7419026898e58f520c6ab32578e"}, + {file = "ruff-0.16.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f33f43a864a8483eebd160e713336c8bab02c934feaff0a33cf5ccb41546d09a"}, + {file = "ruff-0.16.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1dfc6f0088149fb6a362c1c446bcbb3fd2157b3852fe2fa68409276eab9ad9b3"}, + {file = "ruff-0.16.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6553498afc35f580f036030795810b9e6bcea31604b0fd9e8d352795473042e3"}, + {file = "ruff-0.16.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a3b8471dea115d37f123882be852bed13403746d3a76c11de4a19ec5f9ff5a03"}, + {file = "ruff-0.16.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:92e59a70bcbd9d3a5483656da906ec28edfdacfce00afd99edb8b4e9d15644be"}, + {file = "ruff-0.16.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7ae7375f803b5520dc9f546bed7e3a0acb70b91812e9bb4b19927de22f25b77d"}, + {file = "ruff-0.16.10-py3-none-win32.whl", hash = "sha256:2a12e01cb9156c10c466f63b46eaae5ecea28dfbd21b5836353ae498e7d1349a"}, + {file = "ruff-0.16.10-py3-none-win_amd64.whl", hash = "sha256:97f2015c92aa97105b0eab19eb5d224884399281cfc5da86a92db4ab5e7fb2ca"}, + {file = "ruff-0.16.10-py3-none-win_arm64.whl", hash = "sha256:25a65fe998c4e6861ec079ada5826a2fc605e6cbccbe9dcd7fac1f54e791621b"}, + {file = "ruff-0.16.10.tar.gz", hash = "sha256:eff4728c4eaae93f0955cd264d24b2ab348e74bf59986ccf282ba6dc16b3b017"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.54" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "sqlalchemy-2.0.54-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:24ae093dec196ba37fc2beb0316de53e7871d3d246a50faecbbb53034e41ded2"}, + {file = "sqlalchemy-2.0.54-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8cc6532f930c27974e9239e5ce5abebe7600ba9807cea4fcf42f1b6cab18fe7"}, + {file = "sqlalchemy-2.0.54-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e7a76d5dce712ce50435d0f97181eb955ec27d138c004176f01282e063bac52"}, + {file = "sqlalchemy-2.0.54-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f5c09090b1a7c4d389d1431f820931e8df318f82caafc53f9a72c872fef467c5"}, + {file = "sqlalchemy-2.0.54-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:762cfe4d340c56368256d936a98b620a9a5650e49c1c84eba51d6edd17ffefb2"}, + {file = "sqlalchemy-2.0.54-cp310-cp310-win32.whl", hash = "sha256:6b6d4e601c4f6d85e99bb3416107cc9418c5603ca73d4ee0f5f8d79c2a1ed9e8"}, + {file = "sqlalchemy-2.0.54-cp310-cp310-win_amd64.whl", hash = "sha256:03cbf8d9a67da618bd65500a5eb3ddac89caf4c61e99b2f03fa4a1952a0725a9"}, + {file = "sqlalchemy-2.0.54-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d03084f3352dd92048cb19c71d90f116d076c9c7937e0ebc7752c4685de6d38"}, + {file = "sqlalchemy-2.0.54-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92622fbbda1b1fe1632f3402a6e516a93c0e41d9158839c6b3dfb12117f26b72"}, + {file = "sqlalchemy-2.0.54-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5800ddea045c2c860ef1d359a07a3066c7c0c426f45e3abc3874e116cb3c6937"}, + {file = "sqlalchemy-2.0.54-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1019abef05a4b5eafc8eae6fb483167fa28a4dbe5f518d577b744f31a5276a37"}, + {file = "sqlalchemy-2.0.54-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b67749f7da3985a529cefbb1474783cb91ef44371cb9713630bade3de908760d"}, + {file = "sqlalchemy-2.0.54-cp311-cp311-win32.whl", hash = "sha256:2f61a70b3b82e2ec7ad6a4f2301422b9ca93ff06917983e41317bcae878bddf6"}, + {file = "sqlalchemy-2.0.54-cp311-cp311-win_amd64.whl", hash = "sha256:1d887fbd5d248e250807bd801e697fc73e3b44866ce5f093dbc90512e75bde25"}, + {file = "sqlalchemy-2.0.54-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffba7eb2d67c7505e82a0902aa854d8824b74c28a183820d6a8bd3cfd0f812c2"}, + {file = "sqlalchemy-2.0.54-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63cae7210fea9899e0bf35c1f1ae55d3ddd9c6d47cae8b6b43d945afa79dd65b"}, + {file = "sqlalchemy-2.0.54-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:68d994e9b0d0423a02a20039631fa6fcbb7fa829a992f7605025774940305d19"}, + {file = "sqlalchemy-2.0.54-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3de32cc6721eb42c3aad35bcfb244bb7a18f66c00f3582aae6281d6287a339b5"}, + {file = "sqlalchemy-2.0.54-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d31a2bc06a854ee52dd86b455be4df7c750b28817e2d1b884e31fff126c4fd7b"}, + {file = "sqlalchemy-2.0.54-cp312-cp312-win32.whl", hash = "sha256:32de6deded25e8b9b11d07428d496ff24dfbc882b8e990c177266948cb5f3d9e"}, + {file = "sqlalchemy-2.0.54-cp312-cp312-win_amd64.whl", hash = "sha256:d65f8ca742ef1e1e14bc417ef59dc2ddf207a7b66b30cfdc6152447314e030cf"}, + {file = "sqlalchemy-2.0.54-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b374e3bc91e246a942592a98ba6a23be76fff21358b00546ac8c0ebc0fd0e00b"}, + {file = "sqlalchemy-2.0.54-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:31d5458672a6f72db2c087f4a5098b3c8503ea0254186ff29205d63afa9401a4"}, + {file = "sqlalchemy-2.0.54-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cad78d04254967bdbcccbed5e631d88fe4868530946ab0929aa45e9032849518"}, + {file = "sqlalchemy-2.0.54-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:48611087a75d26d798003645c688c7d3cfc26b89dbe4a2c568d6b378d330deae"}, + {file = "sqlalchemy-2.0.54-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d6adf80277372a89910a0f3ccfe960b846d279dc55b366dd5c5ec07f41c84758"}, + {file = "sqlalchemy-2.0.54-cp313-cp313-win32.whl", hash = "sha256:264460333ed0b177cbb1956355d0ee4e0cab83fb415c934ce12a25db2e7be39c"}, + {file = "sqlalchemy-2.0.54-cp313-cp313-win_amd64.whl", hash = "sha256:cf89e92bf0d4204a6afcc17af27b9271ed9c7e34e17d6f80c085d431ea4a1747"}, + {file = "sqlalchemy-2.0.54-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:abd6b21bc58e91c1932eb5d6d7f1bd44a551dfec7b6a7f517c3638ccd67233a0"}, + {file = "sqlalchemy-2.0.54-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5417322b3c025dd82918725d3bf09ec105fac95efc195722b8b06e1d9c381139"}, + {file = "sqlalchemy-2.0.54-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f84099e4b04a5c2d44500a2a8302eee5af4bc6fee63e8c6e9cf6786e747280e"}, + {file = "sqlalchemy-2.0.54-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a0956dc754d3884da7fe60097110ec7a8a105d26afa2f0844468f4b1598c6912"}, + {file = "sqlalchemy-2.0.54-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:87ba8834318b0d8dc94fc6f405d071b5c08be32a6c3fd68107fd6952ee949615"}, + {file = "sqlalchemy-2.0.54-cp314-cp314-win32.whl", hash = "sha256:842540e4382472f23c79589995752648d14696a8200d0807ed8c5c59c92ade44"}, + {file = "sqlalchemy-2.0.54-cp314-cp314-win_amd64.whl", hash = "sha256:f4e8f955d13af83fb4e35c3472e5377ee22d3445eada1e5e48199588edb69835"}, + {file = "sqlalchemy-2.0.54-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ca05f4e7852cf48083b0cf157e4f9504b7068780422a50fa82f45353b8c5e14a"}, + {file = "sqlalchemy-2.0.54-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18a8b6417cbb7b735cf91c2b59453c2a554cefa0a8d7bd15aa35740739410d77"}, + {file = "sqlalchemy-2.0.54-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4e55a0b96a1577a1e108c91ccdeeb9cd92768f28ce206597311c3bf6d6423abd"}, + {file = "sqlalchemy-2.0.54-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:69cab115c40fd02c5a22c68e4ee630fa6ef9a1650f1de944419aab1f7096fc4f"}, + {file = "sqlalchemy-2.0.54-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e08397c6c42f53b2488acde9108b8bfefd52d7afd1bf2f03d2ffcab7a204aceb"}, + {file = "sqlalchemy-2.0.54-cp314-cp314t-win32.whl", hash = "sha256:b9086b8ad48280ef6a7ba68262d5e44f7db1c4cb1973e8cdae8a9f467ae66f51"}, + {file = "sqlalchemy-2.0.54-cp314-cp314t-win_amd64.whl", hash = "sha256:b67c1744e453af833667fc1b84de07adb4a64f3536ef52a8ec5ac2b941d43970"}, + {file = "sqlalchemy-2.0.54-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:330d35f9ce815d35cb1daab038d4d7ec0e907f4d7ed0fc8bcb2411d1f23d0b50"}, + {file = "sqlalchemy-2.0.54-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1f455db400289f77ba2f7b62fffafe8875153812d0e3777aa4ff2b34a0fc1f7"}, + {file = "sqlalchemy-2.0.54-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4e8a4afcc7d714cc3c8a57facdff4c3529f5f93d71e54b7da1e03e022c9089c9"}, + {file = "sqlalchemy-2.0.54-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:34e10af7d274a5c4b7cd0fced5e7361008c5e07d97dd48a93852d5b2f1142a1c"}, + {file = "sqlalchemy-2.0.54-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:7108f410f596c5ac22fe43ba467e864d27c4e1477ae89e90c6c87120b2c1be23"}, + {file = "sqlalchemy-2.0.54-cp38-cp38-win32.whl", hash = "sha256:c1a3455a88f66e4851792bedb098ed942912253d31caed1dbc58afbfa9e875cd"}, + {file = "sqlalchemy-2.0.54-cp38-cp38-win_amd64.whl", hash = "sha256:f3ea33bcf0aa599c1511fe5c9fb126f45aa450419084c4823f786155fe4c79f1"}, + {file = "sqlalchemy-2.0.54-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b6c419c83a87fd901f0b1b5338ffcb82471c3ac32a86bb8883688c18f8eb85d3"}, + {file = "sqlalchemy-2.0.54-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:415239eb2ddbbc508ba4cac97affb91c0f210548fd1731edda6e529b0bb93015"}, + {file = "sqlalchemy-2.0.54-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:279bde5bfedb0f3e0f1bdbcffa2daa39c6c54d90f9408ef3b1802001597199f0"}, + {file = "sqlalchemy-2.0.54-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7b973e4facc2f80e42f5a27b841feb7e202661881a6320580abbe597a28a007f"}, + {file = "sqlalchemy-2.0.54-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:deeab253fe01a770f634c7007c73702df2324c868a79ae756507a9a1a76294fe"}, + {file = "sqlalchemy-2.0.54-cp39-cp39-win32.whl", hash = "sha256:d566099d60cded87d175d4171dc899b9613d2e3b663573364565ca1b27ccd241"}, + {file = "sqlalchemy-2.0.54-cp39-cp39-win_amd64.whl", hash = "sha256:744fb219a390561a57dbbd59cd69a22b5b5b2facfde794c1f79236dd847fa67a"}, + {file = "sqlalchemy-2.0.54-py3-none-any.whl", hash = "sha256:7e33a631ab1474f8fe6b910bd1a07b7b8009c4c78cdd3fb18001b03e3bc2e1d2"}, + {file = "sqlalchemy-2.0.54.tar.gz", hash = "sha256:baa8521e8ee9f24e75dfc7aaabc08020e551ef0d48d7c3e3536f5cddf277586b"}, +] + +[package.dependencies] +greenlet = {version = ">=1", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (>=1)"] +aioodbc = ["aioodbc", "greenlet (>=1)"] +aiosqlite = ["aiosqlite", "greenlet (>=1)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (>=1)"] +asyncmy = ["asyncmy (>=0.2.12)", "greenlet (>=1)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (>=1)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlmodel" +version = "0.0.42" +description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "sqlmodel-0.0.42-py3-none-any.whl", hash = "sha256:a732dab1a40e5cbd6ac250cf2b12e8d9b1bf1a47edf27c34d612f359e8875c63"}, + {file = "sqlmodel-0.0.42.tar.gz", hash = "sha256:9ecec2b6aa4c2aa58da39d2572e7bef4373419dcd6ec52973c07de7be2b1c516"}, +] + +[package.dependencies] +pydantic = ">=2.11.0" +SQLAlchemy = ">=2.0.14,<2.1.0" +typing-extensions = ">=4.5.0" + +[[package]] +name = "starlette" +version = "1.8.0" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.11" +groups = ["main"] +files = [ + {file = "starlette-1.8.0-py3-none-any.whl", hash = "sha256:dfdd6b29c26483288088d990eee59631dedadd66ce20d203402a7ca8e3c4656f"}, + {file = "starlette-1.8.0.tar.gz", hash = "sha256:1565dc0b35d5737a271ed1e0e04e949f4e81198799f216d2667b0a0fb9cf9522"}, +] + +[package.dependencies] +anyio = ">=4.0.0,<5" + +[package.extras] +full = ["httpx (>=0.27.0,<0.29.0)", "httpx2 (>=2.0.0)", "itsdangerous", "jinja2", "opentelemetry-api", "python-multipart (>=0.0.18)", "pyyaml"] + +[[package]] +name = "truststore" +version = "0.10.5" +description = "Verify certificates using native system trust stores" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +markers = "sys_platform != \"emscripten\"" +files = [ + {file = "truststore-0.10.5-py3-none-any.whl", hash = "sha256:9aaaedaefaf06d8b206278cf8b5012bc897f485a874503501e12d776df78951c"}, + {file = "truststore-0.10.5.tar.gz", hash = "sha256:30d36967ccaded5cbb38d602c433f53600036c79d502f4533a49b60a03bbefcd"}, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8"}, + {file = "typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5"}, +] + +[[package]] +name = "typing-inspection" +version = "0.4.4" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147"}, + {file = "typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47"}, +] + +[package.dependencies] +typing-extensions = ">=4.15.0" + +[[package]] +name = "uvicorn" +version = "0.53.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "uvicorn-0.53.0-py3-none-any.whl", hash = "sha256:e8dca71ec86dce5f04e333f0d56cdedf942446e6643b9cea1af0d6d3a02cb03e"}, + {file = "uvicorn-0.53.0.tar.gz", hash = "sha256:a9356f0cb89b3b8621529c5d5eebd69bfe154f4c3f68b4cf2de47e45fa855c2e"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" + +[package.extras] +standard = ["httptools (>=0.8.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.20)", "websockets (>=13.0)"] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.13" +content-hash = "4f9910b73fceef8a03d91a747d4716dfdaa0689c4c667b3a17c8167a8851941e" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..81f131e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,36 @@ +[project] +name = "fdhview" +version = "0.1.0" +description = "Filmhuis Den Haag schedule viewer" +authors = [ + {name = "Your Name",email = "you@example.com"} +] +readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "fastapi (>=0.141.1,<0.142.0)", + "pydantic-xml (>=2.21.1,<3.0.0)", + "sqlmodel (>=0.0.42,<0.0.43)", + "uvicorn (>=0.53.0,<0.54.0)", + "python-multipart (>=0.0.32,<0.0.33)", + "pydantic-settings (>=2.15.0,<3.0.0)", + "aiosqlite (>=0.22.1,<0.23.0)" +] + +[dependency-groups] +dev = [ + "pytest (>=9.1.1,<10.0.0)", + "mypy (>=2.3.1,<3.0.0)", + "ruff (>=0.16.6,<0.17.0)", + "httpx2 (>=2.13.1,<3.0.0)", + "pytest-asyncio (>=1.4.0,<2.0.0)", +] + + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" + + +[tool.ruff.lint.flake8-bugbear] +extend-immutable-calls = ["fastapi.Depends", "fastapi.File", "fastapi.Query", "fastapi.Body", "fastapi.Header", "fastapi.Path"] diff --git a/scripts/inspect_xml.py b/scripts/inspect_xml.py new file mode 100644 index 0000000..a3577fa --- /dev/null +++ b/scripts/inspect_xml.py @@ -0,0 +1,30 @@ +from pathlib import Path + +from fdhview.services.screenwriter.schema import AAMPos + + +def read_xml(): + # xml = Path('tests/data/ScreenWriter_202608181146.xml').read_text() + xml = Path('tests/data/minimal.xml').read_text() + data = AAMPos.from_xml(xml) + + unique_tags = set() + unique_screen = set() + + for session in data.sessions.root: + for tag in session.attributes: + unique_tags.add(tag) + + print(f'Unique tags: {unique_tags}') + + + for session in data.sessions.root: + unique_screen.add(session.screen_identifier) + print(f'Unique screen: {unique_screen}') + + + for session in data.sessions.root: + print(f'Session start: {session.start}, end: {session.end}') + + +read_xml() diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..7011cc5 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,52 @@ +import asyncio +from collections.abc import AsyncGenerator, Iterator + +import pytest +import pytest_asyncio +from fastapi.testclient import TestClient +from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine +from sqlmodel import SQLModel +from sqlmodel.ext.asyncio.session import AsyncSession + +from fdhview.api.deps import get_session +from fdhview.main import app + +test_engine = create_async_engine("sqlite+aiosqlite:///:memory:", echo=False) +test_session_maker = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False) + + +@pytest.fixture(autouse=True) +def setup_test_db() -> Iterator[None]: + """Creates tables before each test and drops them after.""" + async def _setup(): + async with test_engine.begin() as conn: + await conn.run_sync(SQLModel.metadata.create_all) + + async def _teardown(): + async with test_engine.begin() as conn: + await conn.run_sync(SQLModel.metadata.drop_all) + + asyncio.run(_setup()) + yield + asyncio.run(_teardown()) + + +@pytest.fixture +def client() -> Iterator[TestClient]: + """Test client that uses the in-memory database.""" + async def override_get_session(): + async with test_session_maker() as session: + yield session + + app.dependency_overrides[get_session] = override_get_session + with TestClient(app) as test_client: + yield test_client + + app.dependency_overrides.clear() + + +@pytest_asyncio.fixture +async def db_session() -> AsyncGenerator[AsyncSession]: + """Async session for tests that need to seed or query data directly.""" + async with test_session_maker() as session: + yield session diff --git a/tests/data/broken.xml b/tests/data/broken.xml new file mode 100644 index 0000000..6741453 --- /dev/null +++ b/tests/data/broken.xml @@ -0,0 +1,59 @@ + + + + + 12065371 + DenHaag_zaal_3 + Filmhuis Den Haag + 202608201400 + 202608201534 + 5280 + Mulan - EPIC SUMMER + 0 + 40 + fourteen + + 2D + Angst + Animation + Geweld + vanaf 6 jaar + + + + 12065374 + DenHaag_zaal_1 + Filmhuis Den Haag + 202608212030 + 202608212339 + 10980 + Apocalypse Now: The Final Cut - EPIC SUMMER + 0 + 98 + 56 + + 2D + Geweld + Grof taalgebruik + vanaf 16 jaar + War + + + + 12065375 + DenHaag_zaal_3 + Filmhuis Den Haag + 202608211815 + 202608211957 + 5760 + Hearts of Darkness: A Filmmaker's Apocalypse - EPIC SUMMER + 0 + 40 + 23 + + 2D + Documentary + + + + diff --git a/tests/data/minimal.xml b/tests/data/minimal.xml new file mode 100644 index 0000000..ada472d --- /dev/null +++ b/tests/data/minimal.xml @@ -0,0 +1,59 @@ + + + + + 12065371 + DenHaag_zaal_3 + Filmhuis Den Haag + 202608201400 + 202608201534 + 5280 + Mulan - EPIC SUMMER + 0 + 40 + 14 + + 2D + Angst + Animation + Geweld + vanaf 6 jaar + + + + 12065374 + DenHaag_zaal_1 + Filmhuis Den Haag + 202608212030 + 202608212339 + 10980 + Apocalypse Now: The Final Cut - EPIC SUMMER + 0 + 98 + 56 + + 2D + Geweld + Grof taalgebruik + vanaf 16 jaar + War + + + + 12065375 + DenHaag_zaal_3 + Filmhuis Den Haag + 202608211815 + 202608211957 + 5760 + Hearts of Darkness: A Filmmaker's Apocalypse - EPIC SUMMER + 0 + 40 + 23 + + 2D + Documentary + + + + diff --git a/tests/test_admin_api.py b/tests/test_admin_api.py new file mode 100644 index 0000000..20802ec --- /dev/null +++ b/tests/test_admin_api.py @@ -0,0 +1,38 @@ +from pathlib import Path + +from fastapi.testclient import TestClient + +from fdhview.core.config import settings + +AUTH = ("username", settings.api_password) + + +def test_upload_requires_auth(client: TestClient): + response = client.post("/admin/upload") + assert response.status_code == 401 + + +def test_upload_requests_wrong_ext(client: TestClient): + response = client.post( + "/admin/upload", + auth=AUTH, + files={"file": ("test.txt", b"not xml", "text/plain")}) + assert response.status_code == 422 + + +def test_upload_requests_bad_xml(client: TestClient): + response = client.post( + "/admin/upload", + auth=AUTH, + files={"file": ("test.xml", b"not xml", "application/xml")}) + assert response.status_code == 422 + + +def test_upload_succeeds_with_valid_xml(client: TestClient): + minimal_xml = Path("tests/data/minimal.xml").read_bytes() + response = client.post( + "/admin/upload", + auth=AUTH, + files={"file": ("minimal.xml", minimal_xml, "application/xml")}) + assert response.status_code == 200 + assert response.json()['filename'] == "minimal.xml" diff --git a/tests/test_importer.py b/tests/test_importer.py new file mode 100644 index 0000000..aa0f2fd --- /dev/null +++ b/tests/test_importer.py @@ -0,0 +1,49 @@ +from datetime import datetime +from pathlib import Path + +import pytest + +from fdhview.services.screenwriter.importer import _clean_title +from fdhview.services.screenwriter.schema import AAMPos + + +def test_xml(): + xml = Path('tests/data/minimal.xml').read_text() + data = AAMPos.from_xml(xml) + + sessions = data.sessions.root + + assert isinstance(sessions[1].start, datetime) + assert isinstance(sessions[1].end, datetime) + assert len(sessions[1].attributes) == 5 + assert len(sessions[2].attributes) == 2 + + +def test_broken_xml(): + xml = Path('tests/data/broken.xml').read_text() + with pytest.raises(ValueError): + AAMPos.from_xml(xml) + + +def test_clean_title(): + # Basic cases + assert _clean_title("Good Movie - EN subs") == "Good Movie" + assert _clean_title("Good Movie- EN subs") == "Good Movie" + assert _clean_title("Good Movie - en subs") == "Good Movie" + assert _clean_title("Good Movie - NL sub") == "Good Movie" + + # Presentation / talk variations + assert _clean_title("Good Movie - met inleiding") == "Good Movie" + assert _clean_title("Good Movie - met inleiding en nagesprek") == "Good Movie" + assert _clean_title("Good Movie – met nagesprek") == "Good Movie" + assert _clean_title("Good Movie - met voorfilms en nagesprek") == "Good Movie" + assert _clean_title("Good Movie - EN subs met inleiding") == "Good Movie" + + # Stacked suffixes + assert _clean_title("Good Movie - met inleiding - EN subs") == "Good Movie" + + # Titles that should NOT be modified + assert _clean_title("Good Movie") == "Good Movie" + assert _clean_title("De la Comédie-Française") == "De la Comédie-Française" + assert _clean_title("Dikkie Dik (re-release)") == "Dikkie Dik (re-release)" + assert _clean_title("Aguirre - Epic Summer - EN subs") == "Aguirre - Epic Summer" diff --git a/tests/test_screenings_api.py b/tests/test_screenings_api.py new file mode 100644 index 0000000..3be1d48 --- /dev/null +++ b/tests/test_screenings_api.py @@ -0,0 +1,53 @@ +from datetime import datetime, timedelta +from zoneinfo import ZoneInfo + +import pytest +from sqlmodel.ext.asyncio.session import AsyncSession + +from fdhview.core.config import settings +from fdhview.models.enums import Screen +from fdhview.models.screening import Screening + + +@pytest.mark.asyncio +async def test_infodesk_filters_past( + client, db_session: AsyncSession +): + now = datetime.now(tz=ZoneInfo(settings.timezone)) + + # Past film (ended 15 mins ago) + past = Screening( + session=1, screen=Screen.ONE, complex_ident="FDH", + start=now - timedelta(hours=2), end=now - timedelta(minutes=15), + duration=105, title="Past Film", rating=0, seats_total=100, seats_sold=10 + ) + # Currently running film (started 30 mins ago, ends in 60 mins) + running = Screening( + session=2, screen=Screen.TWO, complex_ident="FDH", + start=now - timedelta(minutes=30), end=now + timedelta(minutes=60), + duration=90, title="Running Film", rating=0, seats_total=100, seats_sold=50 + ) + # Upcoming film (starts in 2 hours) + upcoming = Screening( + session=3, screen=Screen.THREE, complex_ident="FDH", + start=now + timedelta(hours=2), end=now + timedelta(hours=4), + duration=120, title="Upcoming Film", rating=0, seats_total=100, seats_sold=20 + ) + + db_session.add_all([past, running, upcoming]) + await db_session.commit() + + response = client.get("/api/screenings") + assert response.status_code == 200 + + titles = [s["title"] for s in response.json()] + assert "Past Film" not in titles + assert "Running Film" not in titles + assert "Upcoming Film" in titles + + +@pytest.mark.asyncio +async def test_get_screening_prices(client): + response = client.get("/api/screenings/prices") + assert response.status_code == 200 + assert response.json() == settings.prices