Compare commits

..

95 commits

Author SHA1 Message Date
7d0d6011d7 CI: switch to Woodpecker 2025-06-27 18:06:27 +02:00
ec4cc9a58a CI: fix container version
Some checks failed
/ test (push) Has been cancelled
/ build (push) Has been cancelled
2025-03-16 14:21:50 +01:00
7105edecd1 CI: build container from plain Python image
All checks were successful
/ test (push) Successful in 25s
/ build (push) Successful in 27s
2025-03-16 14:14:39 +01:00
ea7e3087cd Use dynamic versioning
Some checks failed
/ test (push) Successful in 26s
/ build (push) Failing after 8s
2025-03-15 17:43:07 +01:00
c7deb34bae Add annotations to kube 2025-03-15 16:30:07 +01:00
3745cfe3f0 Add kube yaml
All checks were successful
/ test (push) Successful in 25s
2024-12-08 06:12:18 +01:00
8bfb410ee1 CI: set db host for tests
All checks were successful
/ test (push) Successful in 25s
/ build (push) Successful in 6s
2024-12-08 05:57:04 +01:00
4a54a815a0 Config: set default db host to localhost
Some checks failed
/ test (push) Failing after 25s
/ build (push) Has been skipped
2024-12-08 05:32:00 +01:00
727698527e CI: split test and build workflows, run build only on git push tag
All checks were successful
/ test (push) Successful in 25s
/ build (push) Successful in 6s
2024-12-08 02:46:31 +01:00
d0c7e4614a Fix loose config with environment variables
Some checks failed
/ test (push) Successful in 24s
/ build (push) Failing after 6s
2024-12-08 02:23:30 +01:00
53f935b916 CI: build container with version and latest tags
All checks were successful
/ test (push) Successful in 25s
/ build (push) Successful in 7s
2024-12-06 20:14:42 +01:00
ed6c70e1a9 CI: publish container with tag latest along with the version
Some checks failed
/ test (push) Successful in 24s
/ build (push) Failing after 5s
2024-12-06 20:07:13 +01:00
6601a6ced3 CI: fix add Python package build
All checks were successful
/ test (push) Successful in 25s
/ build (push) Successful in 6s
2024-12-06 05:08:05 +01:00
ffd3e99ecc CI: add Python package build
Some checks failed
/ test (push) Successful in 25s
/ build (push) Failing after 4s
2024-12-06 05:04:39 +01:00
48507cc01c Add database container image build 2024-12-06 05:03:55 +01:00
425aaf9dc0 Playbook: fix build and publish ci image
Some checks failed
/ test (push) Successful in 47s
/ build (push) Failing after 1s
2024-12-06 03:35:08 +01:00
c97e3123c9 Playbook: build and publish ci image 2024-12-06 03:34:01 +01:00
a1a499b370 Containers: use public registry
Some checks failed
/ test (push) Failing after 1s
/ build (push) Has been skipped
2024-12-06 03:16:21 +01:00
23afaa7c82 CI: build python package
Some checks failed
/ test (push) Failing after 1s
/ build (push) Has been skipped
2024-12-06 03:05:43 +01:00
5c27a26e78 Update registry and containers for CI
All checks were successful
/ test (push) Successful in 25s
/ build (push) Successful in 15s
2024-12-05 18:59:55 +01:00
259f881815 CI: full image name for the database service 2
Some checks failed
/ test (push) Successful in 25s
/ build (push) Failing after 2s
2024-11-04 23:15:01 +01:00
6abdb5e0d6 CI: full image name for the database service
All checks were successful
/ test (push) Successful in 25s
/ build (push) Successful in 1s
2024-11-04 13:39:38 +01:00
e77bb9eb54 CI: fix app version fix
All checks were successful
/ test (push) Successful in 17s
/ build (push) Successful in 11s
2024-11-04 04:39:06 +01:00
a23674ff63 CI: fix app version 3
All checks were successful
/ test (push) Successful in 17s
/ build (push) Successful in 1s
2024-11-04 04:36:12 +01:00
c1ffb17162 CI: fix app version 2
All checks were successful
/ test (push) Successful in 17s
/ build (push) Successful in 1m29s
2024-11-04 04:17:55 +01:00
d1b7357de4 CI: fix app version
All checks were successful
/ test (push) Successful in 17s
/ build (push) Successful in 11s
2024-11-04 04:10:09 +01:00
1693662e75 Version file added dynamically (workaround as uv does not have this feature yet)
All checks were successful
/ test (push) Successful in 17s
/ build (push) Successful in 19s
Tag container image with playbook
Importable module with python -m treetrail
2024-11-04 03:58:58 +01:00
2d0b788728 Cleanup
All checks were successful
/ test (push) Successful in 17s
/ build (push) Successful in 2s
2024-11-03 19:44:10 +01:00
5a9f28dfb4 CI: default not to build container image manually
All checks were successful
/ test (push) Successful in 16s
/ build (push) Successful in 11s
2024-11-03 19:34:33 +01:00
5ef71e1f17 CI: fix file name
All checks were successful
/ test (push) Successful in 17s
/ build (push) Successful in 2s
2024-11-03 19:32:05 +01:00
93d414673c CI: build only if the test pass 2024-11-03 19:26:43 +01:00
b523ead55f CI: build container when the version is a clean tag
All checks were successful
/ build (push) Successful in 10s
/ test (push) Successful in 17s
2024-11-03 19:12:28 +01:00
cc4584ae97 CI: build container image on request only
All checks were successful
/ test (push) Successful in 18s
2024-11-03 04:57:44 +01:00
6e5711bfd1 CI: fix build 4
All checks were successful
/ build (push) Successful in 18s
/ test (push) Successful in 17s
2024-11-03 04:51:32 +01:00
8a4c64775a CI: fix build 3
Some checks failed
/ build (push) Has been cancelled
/ test (push) Has been cancelled
2024-11-03 04:11:35 +01:00
4b0fca68c4 CI: fix build 1
Some checks failed
/ build (push) Has been cancelled
/ test (push) Successful in 16s
2024-11-03 03:37:24 +01:00
48179db69b CI: fix build
Some checks failed
/ build (push) Failing after 16s
/ test (push) Successful in 16s
2024-11-03 02:39:28 +01:00
c5ce114282 Cosmetic 2024-11-03 02:38:45 +01:00
c783be5155 Reduce container size 2024-11-03 02:33:57 +01:00
21bd7a904f CI: split workflow in 2 jobs
All checks were successful
/ test (push) Successful in 30s
/ build (push) Has been skipped
2024-11-03 02:18:01 +01:00
51d131600e Build containers from custom registry 2024-11-03 02:17:02 +01:00
d552656e95 CI: don't build the container image by default
All checks were successful
/ build (push) Successful in 20s
2024-11-02 05:30:27 +01:00
056d86ca36 CI: optimise (use container with all deps for the runner) fix 2
Some checks failed
/ build (push) Failing after 47s
2024-11-02 05:23:45 +01:00
e8ed03bcba CI: optimise (use container with all deps for the runner) fix
Some checks failed
/ build (push) Failing after 17s
2024-11-02 05:22:43 +01:00
32e499e2cc CI: optimise (use container with all deps for the runner)
Some checks failed
/ build (push) Failing after 14s
2024-11-02 05:21:05 +01:00
50a2a2ec8f CI: cosmetic
Some checks failed
/ build (push) Failing after 12s
2024-11-01 23:26:53 +01:00
518ee39dac Merge branch 'master'
Some checks failed
/ build (push) Failing after 13s
2024-11-01 23:25:40 +01:00
0f612ae3cc Container build: simplify & speed up, removing the final step running the app as a dedicated user 2024-11-01 23:24:44 +01:00
3e2e9df890 CI: avoid premature failure when loggin in to registry
Some checks failed
/ build (push) Failing after 14s
2024-11-01 13:59:33 +01:00
ae0d3532aa Change default location
Some checks failed
/ build (push) Failing after 12s
2024-11-01 06:00:37 +01:00
7c33cb0b99 Ansible build: force building app container
Some checks failed
/ build (push) Failing after 12s
2024-11-01 05:32:40 +01:00
e5a2048223 Add OpenFreeMap background in default config
Some checks failed
/ build (push) Failing after 13s
2024-11-01 05:05:06 +01:00
114201d561 Add playbook for building the images
Some checks failed
/ build (push) Failing after 15s
2024-10-30 02:07:05 +01:00
9ca2ea638e Build: use registry for container image sources
Some checks failed
/ build (push) Failing after 36s
2024-10-29 04:21:04 +01:00
9844583050 CI: build: fix registry login 9
Some checks failed
/ build (push) Failing after 35s
2024-10-29 04:09:46 +01:00
cc5ddc91c5 CI: build: fix registry login 8
Some checks failed
/ build (push) Failing after 24s
2024-10-29 04:07:43 +01:00
bef203bb4d CI: build: fix registry login 7
Some checks failed
/ build (push) Failing after 25s
2024-10-29 03:54:33 +01:00
423cd92565 CI: build: fix registry login 6
Some checks failed
/ build (push) Failing after 25s
2024-10-29 03:38:14 +01:00
bb3c589f71 CI: build: fix registry login 5
Some checks failed
/ build (push) Has been cancelled
2024-10-29 03:33:57 +01:00
ac664886e3 CI: build: fix registry login 4
Some checks failed
/ build (push) Failing after 29s
2024-10-29 03:27:52 +01:00
4e861c270f CI: build: fix registry login 2
Some checks failed
/ build (push) Failing after 2s
2024-10-29 02:48:42 +01:00
c4f5a73a49 CI: build: fix registry login
Some checks failed
/ build (push) Failing after 27s
2024-10-28 18:40:34 +01:00
bbbbf67ba5 CI: build: fix variable name
Some checks failed
/ build (push) Failing after 26s
2024-10-28 18:37:55 +01:00
f2251d464e CI: build: add workflow_dispatch 2
Some checks failed
/ build (push) Has been cancelled
2024-10-28 18:34:38 +01:00
5e8abadb07 CI: build: add workflow_dispatch
Some checks failed
/ build (push) Failing after 27s
2024-10-28 18:33:23 +01:00
87cdbbc271 CI: build: login to registry step - use local mirrored action
Some checks failed
/ build (push) Failing after 27s
2024-10-28 18:28:13 +01:00
d59a02b884 CI: build: login to registry step
Some checks failed
/ build (push) Failing after 7s
2024-10-28 18:11:26 +01:00
8a74886369 CI: improve workflow debug 12
Some checks failed
/ build (push) Failing after 32s
2024-10-28 13:54:42 +01:00
f3e917ff98 CI: improve workflow debug 11
Some checks failed
/ build (push) Failing after 22s
2024-10-28 11:59:40 +01:00
0393e50fc7 CI: improve workflow debug 10
Some checks failed
/ build (push) Failing after 21s
2024-10-28 11:54:42 +01:00
9fdaa03f7b CI: improve workflow debug 9
Some checks failed
/ build (push) Failing after 21s
2024-10-28 11:51:20 +01:00
6051a52dba CI: improve workflow debug 8
Some checks failed
/ build (push) Failing after 21s
2024-10-28 11:45:53 +01:00
28d24fc11a CI: improve workflow debug 7
Some checks failed
/ build (push) Failing after 22s
2024-10-28 11:44:06 +01:00
fa266814a6 CI: improve workflow debug 6
Some checks failed
/ build (push) Failing after 25s
2024-10-28 11:38:51 +01:00
54b374576f CI: improve workflow debug 5
Some checks failed
/ build (push) Failing after 15s
2024-10-28 10:47:50 +01:00
0d05bd5c7e CI: improve workflow debug 4
Some checks failed
/ build (push) Failing after 21s
2024-10-28 10:43:31 +01:00
9b243bd044 CI: improve workflow debug 3
Some checks failed
/ build (push) Failing after 21s
2024-10-28 10:37:11 +01:00
2927d11bf8 CI: improve workflow debug 2
Some checks failed
/ build (push) Failing after 21s
2024-10-28 10:36:26 +01:00
715dbe5363 CI: improve workflow debug 1
Some checks failed
/ build (push) Failing after 21s
2024-10-27 14:59:45 +01:00
735ad5dd9c CI: improve workflow
Some checks failed
/ build (push) Failing after 25s
2024-10-27 13:35:16 +01:00
9c9b26d036 Exclude more artifacts when building containers
Some checks failed
/ build (push) Failing after 22s
2024-10-27 05:33:24 +01:00
34bd44af5b Cleanup
Some checks failed
/ build (push) Failing after 22s
2024-10-27 05:31:44 +01:00
59ac5617cd Create .containerignore for excluding .venv
Some checks failed
/ build (push) Failing after 22s
2024-10-27 05:26:08 +01:00
9312a2b37d CI: add action using treetrail-backend-ci
Some checks failed
/ build (push) Failing after 22s
2024-10-27 04:36:35 +01:00
5b5258428b Initial commit
Some checks failed
/ build (push) Failing after 23s
2024-10-27 02:09:13 +02:00
1da1965b3c Merge branch 'master' 2024-10-26 18:04:49 +02:00
d26464130c Remove regex warning 2024-10-26 18:03:26 +02:00
4f9fdbe34a CI: rename deps Containerfile 2024-10-26 14:15:23 +02:00
f6f5507245 CI: rename container 2024-10-26 14:13:16 +02:00
53c8996a3b CI: use specfic Containerfile for the runner to build the app image 2024-10-26 00:33:57 +02:00
01341a0cd9 Change Containerfile.backend_deps to use uv 2024-10-26 00:19:54 +02:00
57a49840b2 Merge branch 'master' 2024-10-25 14:05:47 +02:00
68a1fecf26 CI: build container 2024-10-25 14:03:43 +02:00
ee5bc1b432 Add debug log for database connection at boot 2024-10-24 16:40:36 +02:00
720fb0ae57 Fix base uri for sprites 2024-10-24 16:39:33 +02:00
28 changed files with 881 additions and 222 deletions

4
.containerignore Normal file
View file

@ -0,0 +1,4 @@
.venv
dist
.pytest_cache
.forgejo

View file

@ -1,19 +0,0 @@
on: [push]
jobs:
install:
runs-on: container
container:
image: tiptop:5000/treetrail-backend-ci-base
volumes:
- "/root/.cache/uv:uv_cache"
services:
treetrail-database:
image: treetrail-database
steps:
- uses: actions/checkout@v4
- name: Create venv
run: uv venv
- name: Install dependencies
run: uv sync
- name: Run basic test (bootstrap)
run: .venv/bin/pytest -s tests/basic.py

70
.woodpecker/build.yaml Normal file
View file

@ -0,0 +1,70 @@
when:
- event: manual
- event: tag
depends_on:
- test
steps:
python_sync:
image: code.philo.ydns.eu/philorg/uv
volumes:
- uv-cache:/uv-cache
environment:
UV_CACHE_DIR: /uv-cache
UV_LINK_MODE: copy
commands:
- uv sync
python_build:
image: code.philo.ydns.eu/philorg/uv
volumes:
- uv-cache:/uv-cache
environment:
UV_CACHE_DIR: /uv-cache
UV_LINK_MODE: copy
commands:
- uv build --wheel
- uv cache prune --ci
python_publish:
image: code.philo.ydns.eu/philorg/uv
volumes:
- uv-cache:/uv-cache
environment:
UV_CACHE_DIR: /uv-cache
UV_LINK_MODE: copy
environment:
OWNER: philorg
REGISTRY_URL: https://code.philo.ydns.eu
REGISTRY_TOKEN:
from_secret: registry_token
commands:
- uv publish --publish-url $REGISTRY_URL/api/packages/$OWNER/pypi --token $REGISTRY_TOKEN dist/*.whl
failure: ignore
container_build_publish:
image: quay.io/podman/stable:latest
# Caution: This image is built daily. It might fill up your image store quickly.
#pull: true
volumes:
- containers:/var/lib/containers
- uv-cache:/uv-cache
# Fill in the trusted checkbox in Woodpecker's settings as well
privileged: true
environment:
UV_CACHE_DIR: /uv-cache
UV_LINK_MODE: copy
registry: code.philo.ydns.eu
org: philorg
container_name: treetrail-backend
registry_token:
from_secret: registry_token
commands:
# Login at the registry
- podman login -u __token__ --password $registry_token $registry
# Build the container image
- podman build --volume=/var/lib/containers:/var/lib/containers --tag $registry/$org/$container_name:latest --tag $registry/$org/$container_name:$CI_COMMIT_TAG .
# Push the image
- podman push $registry/$org/$container_name:latest
- podman push $registry/$org/$container_name:$CI_COMMIT_TAG

21
.woodpecker/test.yaml Normal file
View file

@ -0,0 +1,21 @@
when:
- event: push
branch: main
- event: manual
- event: tag
steps:
sync:
image: code.philo.ydns.eu/philorg/uv
volumes:
- uv-cache:/uv-cache
environment:
UV_CACHE_DIR: /uv-cache
UV_LINK_MODE: copy
commands:
- uv sync
test:
image: code.philo.ydns.eu/philorg/uv
commands:
- .venv/bin/pytest -s tests/basic.py

View file

@ -1,12 +1,17 @@
FROM localhost/trixie_python FROM docker.io/library/python:latest
WORKDIR /usr/src/treetrail
ENV PATH="/usr/src/treetrail/.venv/bin:$PATH"
ENV PYTHONPATH="/usr/src"
COPY --from=localhost/treetrail_backend_deps /usr/src/treetrail/.venv/ /usr/src/treetrail/.venv
COPY --from=localhost/treetrail_backend_deps /usr/local/treetrail/ /usr/local/treetrail
COPY ./treetrail ./pyproject.toml ./README.md .
# Instances should override the prod.yaml file COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/
COPY ./prod.yaml /etc/treetrail/prod.yaml
CMD ["uvicorn", "treetrail.application:app", "--port", "8081", "--log-config", "logging.yaml", "--host", "0.0.0.0"] COPY . /app
# Sync the project into a new environment, using the frozen lockfile
WORKDIR /app
RUN uv pip install --system .
CMD [ \
"uvicorn", "treetrail.application:app", \
"--port", "8081", \
#"--log-config", "/app/config/logging.yaml", \
"--host", "0.0.0.0" \
]

View file

@ -1,7 +0,0 @@
FROM debian:trixie-slim
MAINTAINER philo email phil.dev@philome.mooo.com
RUN apt update
RUN apt install --no-install-recommends -y python-is-python3 python3-pip python3-venv nodejs git
RUN pip install --break-system-packages uv
RUN apt-get clean && rm -rf /var/lib/apt/lists/* && rm -rf /root/.cache

View file

@ -1,36 +0,0 @@
FROM localhost/trixie_python
MAINTAINER philo email phil.dev@philome.mooo.com
#ENV PROJ_DIR=/usr
ENV PYTHONDONTWRITEBYTECODE 1
ENV PDM_CHECK_UPDATE=false
#RUN apk add --no-cache make cmake clang gdal-dev geos-dev proj-dev proj-util gcc musl-dev bash
#RUN apk add --no-cache gdal-dev geos-dev proj-dev proj-util gcc musl-dev bash
WORKDIR /usr/src/treetrail
COPY ./pyproject.toml ./README.md ./pdm.lock .
# Cheating pdm with the app version to allow install of dependencies
RUN PDM_BUILD_SCM_VERSION=1.0 pdm install --check --prod --no-editable
## Instances should populate these dirs below
RUN mkdir -p /usr/local/treetrail/osm \
/usr/local/treetrail/sprite \
/usr/local/treetrail/cache/plantekey/img \
/usr/local/treetrail/cache/plantekey/thumbnails \
/usr/local/treetrail/cache/plantekey/type \
/usr/local/treetrail/map/sprite \
/usr/local/treetrail/map/osm \
/usr/local/treetrail/attachments/tree \
/usr/local/treetrail/attachments/trail \
/usr/local/treetrail/attachments/poi
#COPY ./sprite /usr/local/treetrail
#COPY ./osm /usr/local/treetrail
#RUN python -c 'import _version as v;print(v.__version__)' > version.txt
#RUN PDM_BUILD_SCM_VERSION=$(cat version.txt) pdm install --check --prod --no-editable
#
# Clear some space (caches)
#RUN pdm cache clear
#RUN rm -rf .mypy_cache
#RUN rm -rf __pycache__

10
Containerfile.ci Normal file
View file

@ -0,0 +1,10 @@
# Build: podman build -t code.philo.ydns.eu/philorg/treetrail-backend-ci -f Containerfile.ci
FROM code.philo.ydns.eu/philorg/python-ci
COPY ./pyproject.toml ./README.md ./uv.lock /_lock/
RUN --mount=type=cache,target=/root/.cache <<EOT
cd /_lock
uv sync --locked --no-dev --no-install-project
EOT

4
Containerfile.database Normal file
View file

@ -0,0 +1,4 @@
FROM docker.io/postgis/postgis:17-3.5-alpine
ENV POSTGRES_USER treetrail
ENV POSTGRES_PASSWORD treetrail

10
Containerfile.deps Normal file
View file

@ -0,0 +1,10 @@
# Build: podman build -t code.philo.ydns.eu/philorg/treetrail-backend-deps -f Containerfile.deps
FROM code.philo.ydns.eu/philorg/trixie_python
COPY ./pyproject.toml ./README.md ./uv.lock /_lock/
RUN --mount=type=cache,target=/root/.cache <<EOT
cd /_lock
uv sync --locked --no-dev --no-install-project
EOT

30
Containerfile.full_copy Normal file
View file

@ -0,0 +1,30 @@
# Build: podman build -t code.philo.ydns.eu/philorg/treetrail-backend-full -f Containerfile.full_copy
FROM code.philo.ydns.eu/philorg/trixie_python
ENV PYTHONPATH $UV_PROJECT_ENVIRONMENT/lib/python3.12/site-packages
ENV PATH=/app/bin:$PATH
RUN <<EOT
groupadd -r app
useradd -r -d /app -g app -N app
mkdir /var/lib/treetrail
chown app: /var/lib/treetrail
EOT
COPY --from=localhost/treetrail-backend-base --chown=app:app /app /app
USER app
WORKDIR /app
# Instances should override the prod.yaml file
#COPY ./prod.yaml /etc/treetrail/prod.yaml
#COPY ./dist/treetrail_backend-0.3.0.tar.gz /src/
#RUN uv pip install /src/treetrail_backend-0.3.0.tar.gz
CMD [ \
"uvicorn", "treetrail.application:app", \
"--port", "8081", \
#"--log-config", "/app/config/logging.yaml", \
"--host", "0.0.0.0" \
]

View file

@ -1,11 +1,23 @@
FROM debian:trixie-slim # Build: podman build -t code.philo.ydns.eu/philorg/trixie_python -f Containerfile.trixie_python
MAINTAINER philo email phil.dev@philome.mooo.com
RUN apt update FROM docker.io/library/debian:trixie-slim
RUN apt install --no-install-recommends -y python-is-python3 python3-pip python3-venv
RUN pip install --break-system-packages pdm
RUN apt-get clean && \ RUN <<EOT
rm -rf /var/lib/apt/lists/* apt-get update -qy
apt-get install -qyy \
-o APT::Install-Recommends=false \
-o APT::Install-Suggests=false \
ca-certificates \
python-is-python3
apt-get clean
EOT
RUN rm -rf /root/.cache COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
ENV UV_LINK_MODE=copy \
UV_COMPILE_BYTECODE=1 \
UV_PYTHON_DOWNLOADS=never \
#UV_PYTHON=python3.12 \
UV_PROJECT_ENVIRONMENT=/app \
PYTHONPATH=/app/lib/python3.12/site-packages \
PATH=/app/bin:$PATH

View file

@ -1,3 +1,6 @@
*Tree Trail* is a fun and pedagogic tool to discover the trails and trees around. *Tree Trail* is a fun and pedagogic tool to discover the trails and trees around.
This is the server (back-end), written in Python. This is the server (back-end), written in Python.
[![status-badge](https://code.philo.ydns.eu/woodpecker/api/badges/20/status.svg)](https://code.philo.ydns.eu/woodpecker/repos/20)

93
build.yaml Normal file
View file

@ -0,0 +1,93 @@
- name: Build containers images
hosts: localhost
gather_facts: false
vars:
force_rm: false
cache: false
repository: code.philo.ydns.eu
organisation: philorg
tasks:
#- name: Read conf
# ansible.builtin.include_vars:
# file: prod.yaml
# name: conf
- name: Build the database image
tags: db
containers.podman.podman_image:
name: treetrail-database
state: build
path: "{{ playbook_dir }}"
build:
format: oci
force_rm: "{{ force_rm }}"
cache: "{{ cache }}"
file: Containerfile.database
push: true
push_args:
dest: "{{ repository }}/{{ organisation }}"
- name: Using the variables
ansible.builtin.debug:
var: force_rm
- name: Get the version from git
tags:
- ci
- deps
- backend
command: git describe --dirty --tags
register: version
args:
chdir: "{{ playbook_dir }}"
- name: Build the base CI image
tags: ci
containers.podman.podman_image:
name: treetrail-backend-ci
tag: "{{ version.stdout }}"
state: build
path: "{{ playbook_dir }}"
build:
format: oci
force_rm: "{{ force_rm }}"
cache: "{{ cache }}"
file: Containerfile.ci
push: true
push_args:
dest: "{{ repository }}/{{ organisation }}"
- name: Build the base image, only with python dependencies
tags: deps
containers.podman.podman_image:
name: treetrail-backend-deps
tag: "{{ version.stdout }}"
state: build
path: "{{ playbook_dir }}"
build:
format: oci
force_rm: "{{ force_rm }}"
cache: "{{ cache }}"
file: Containerfile.deps
push: true
push_args:
dest: "{{ repository }}/{{ organisation }}"
- name: Build the backend container image
tags: backend
containers.podman.podman_image:
name: treetrail-backend
tag: "{{ version.stdout }}"
state: build
force: true
path: "{{ playbook_dir }}"
build:
format: oci
force_rm: "{{ force_rm }}"
cache: "{{ cache }}"
file: Containerfile
extra_args: "--build-arg APP_VERSION={{ version.stdout }}"
push: true
push_args:
dest: "{{ repository }}/{{ organisation }}"

View file

@ -1,77 +1,80 @@
[project] [project]
name = "treetrail-srv" name = "treetrail-backend"
version = "0.3.0" dynamic = ["version"]
#dynamic = ["version"]
#dynamic = ["version"]
description = "A fun and pedagogic tool to discover the trails and trees around" description = "A fun and pedagogic tool to discover the trails and trees around"
authors = [ authors = [{ name = "Philippe May", email = "phil.treetrail@philome.mooo.com" }]
{ name = "Philippe May", email = "phil.treetrail@philome.mooo.com" }
]
dependencies = [ dependencies = [
"aiofiles", "aiofiles",
"aiohttp-client-cache", "aiohttp-client-cache",
"aiosqlite", "aiosqlite",
"asyncpg", "asyncpg",
"fastapi", "fastapi",
"geoalchemy2", "geoalchemy2",
"geopandas", "geopandas",
"httptools>=0.6.1", "httptools>=0.6.1",
"orjson", "orjson",
"pandas", "pandas",
"passlib[bcrypt]", "passlib[bcrypt]",
"pillow", "pillow",
"psycopg2-binary", "psycopg2-binary",
"pyarrow", "pyarrow>=19.0.1",
"pydantic-settings", "pydantic-settings",
"python-jose[cryptography]", "python-jose[cryptography]",
"python-multipart", "python-multipart",
"requests", "requests",
"sqlalchemy[asyncio]", "sqlalchemy[asyncio]",
"sqlmodel", "sqlmodel",
"uvicorn[standard]", "uvicorn[standard]",
"uvloop", "uvloop",
] ]
requires-python = ">=3.11" requires-python = ">=3.11"
readme = "README.md" readme = "README.md"
license = {text = "MIT"} license = { text = "MIT" }
classifiers = [ classifiers = [
"Development Status :: 3 - Alpha", "Development Status :: 3 - Alpha",
"Framework :: FastAPI", "Framework :: FastAPI",
"Environment :: Web Environment", "Environment :: Web Environment",
"Intended Audience :: Developers", "Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License (GPL)", "License :: OSI Approved :: GNU General Public License (GPL)",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Operating System :: MacOS :: MacOS X", "Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX", "Operating System :: POSIX",
"Programming Language :: Python", "Programming Language :: Python",
] ]
#[project.scripts] #[project.scripts]
#treetrail-srv = "treetrail_srv:main" #treetrail-backend = "treetrail_backend:main"
[dependency-groups]
dev = ["dunamai>=1.23.0", "ipdb>=0.13.13"]
[build-system] [build-system]
requires = ["hatchling"] requires = ["hatchling", "uv-dynamic-versioning"]
build-backend = "hatchling.build" build-backend = "hatchling.build"
[tool.hatch.version]
source = "uv-dynamic-versioning"
[tool.hatch.build.targets.wheel] [tool.hatch.build.targets.wheel]
packages = ["src/treetrail"] packages = ["src/treetrail"]
[tool.uv-dynamic-versioning]
style = "semver"
[tool.uv] [tool.uv]
package = true
dev-dependencies = [ dev-dependencies = [
"httpx", "httpx",
"ipdb", "ipdb",
"pandas-stubs", "pandas-stubs",
"pytest", "pytest",
"types-Pillow", "types-Pillow",
"types-PyYAML", "types-PyYAML",
"types-aiofiles", "types-aiofiles",
"types-passlib", "types-passlib",
"types-python-jose", "types-python-jose",
"types-requests", "types-requests",
] ]
#[tool.pdm.version] [tool.black]
#source = "scm" line-length = 98
#write_to = "treetrail/_version.py"
#write_template = "__version__ = '{}'"
#

View file

@ -0,0 +1,8 @@
import importlib.metadata
try:
from dunamai import Version, Style
__version__ = Version.from_git().serialize(style=Style.SemVer, dirty=True)
except (ImportError, RuntimeError):
__version__ = importlib.metadata.version(__name__)

View file

@ -0,0 +1,3 @@
from .application import _main
_main()

View file

@ -1 +0,0 @@
__version__ = '0.3.0'

View file

@ -36,7 +36,8 @@ from treetrail.models import (BaseMapStyles, User, Role, Bootstrap,
MapStyle, Tree, Trail, MapStyle, Tree, Trail,
TreeTrail, POI, UserWithRoles, Zone, TreeTrail, POI, UserWithRoles, Zone,
VersionedComponent) VersionedComponent)
from treetrail.config import conf, get_cache_dir, __version__ from treetrail.config import conf, get_cache_dir
from treetrail import __version__
from treetrail.plantekey import get_local_details from treetrail.plantekey import get_local_details
from treetrail.tiles import registry as tilesRegistry from treetrail.tiles import registry as tilesRegistry
@ -50,7 +51,7 @@ api_app = FastAPI(
default_response_class=responses.ORJSONResponse, default_response_class=responses.ORJSONResponse,
) )
re_findmimetype = re.compile('^data:(\S+);') # type: ignore re_findmimetype = re.compile(r'^data:(\S+);') # type: ignore
attachment_types: dict[str, type[Tree] | type[Trail] | type[POI]] = { attachment_types: dict[str, type[Tree] | type[Trail] | type[POI]] = {
'tree': Tree, 'tree': Tree,

View file

@ -51,22 +51,7 @@ app.mount(
def _main(argv=None): def _main(argv=None):
from argparse import ArgumentParser from argparse import ArgumentParser
arg_parser = ArgumentParser( arg_parser = ArgumentParser(
description="fastapi Application server", description="Treetrail backend / server",
prog="fastapi"
)
arg_parser.add_argument(
'--path',
help='Path of socket file',
)
arg_parser.add_argument(
"-H", "--hostname",
help="TCP/IP hostname to serve on (default: %(default)r)",
default="localhost"
)
arg_parser.add_argument(
"-P", "--port",
help="TCP/IP port to serve on",
type=int,
) )
arg_parser.add_argument( arg_parser.add_argument(
"-c", "--create-db", "-c", "--create-db",
@ -154,6 +139,11 @@ def _main(argv=None):
help="Set debug logging", help="Set debug logging",
action="store_true" action="store_true"
) )
arg_parser.add_argument(
"--version",
help="Print version and exit",
action="store_true"
)
args = arg_parser.parse_args() args = arg_parser.parse_args()
if args.debug: if args.debug:
@ -161,6 +151,11 @@ def _main(argv=None):
## For ipdb: ## For ipdb:
logging.getLogger('parso').setLevel(logging.WARNING) logging.getLogger('parso').setLevel(logging.WARNING)
if args.version:
import treetrail
print(treetrail.__version__)
sys.exit(0)
if args.create_db: if args.create_db:
from treetrail.database import create_db from treetrail.database import create_db
import asyncio import asyncio
@ -233,7 +228,8 @@ def _main(argv=None):
sys.exit(0) sys.exit(0)
print( print(
'This application needs to be run with an asgi server like uvicorn.', 'No CLI option was given, try --help',
'To run the server, use an asgi server like uvicorn.',
'For example:', 'For example:',
'uvicorn application:app', 'uvicorn application:app',
'or:', 'or:',

View file

@ -12,8 +12,6 @@ from pydantic_settings import (
) )
from pydantic.v1.utils import deep_update from pydantic.v1.utils import deep_update
from treetrail._version import __version__
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
ENV = environ.get("env", "prod") ENV = environ.get("env", "prod")
@ -65,16 +63,11 @@ def create_dirs():
def get_cache_dir() -> Path: def get_cache_dir() -> Path:
return Path(conf.storage.root_cache_path) return Path(conf.storage.root_cache_path)
class MyBaseSettings(BaseSettings):
model_config = SettingsConfigDict(
env_prefix='treetrail_',
env_nested_delimiter="_",
)
class DB(BaseSettings):
class DB(MyBaseSettings): model_config = SettingsConfigDict(env_prefix="treetrail_db_")
# uri: str # uri: str
host: str = "treetrail-database" host: str = "localhost"
port: int = 5432 port: int = 5432
user: str = "treetrail" user: str = "treetrail"
db: str = "treetrail" db: str = "treetrail"
@ -92,53 +85,62 @@ class DB(MyBaseSettings):
return f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.db}" return f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.db}"
class App(MyBaseSettings): class App(BaseSettings):
model_config = SettingsConfigDict(env_prefix="treetrail_app_")
title: str = "Tree Trail" title: str = "Tree Trail"
class Storage(MyBaseSettings): class Storage(BaseSettings):
model_config = SettingsConfigDict(env_prefix="treetrail_storage_")
root_attachment_path: str = "/var/lib/treetrail/attachments" root_attachment_path: str = "/var/lib/treetrail/attachments"
root_cache_path: str = "/var/lib/treetrail/cache" root_cache_path: str = "/var/lib/treetrail/cache"
class Tiles(MyBaseSettings): class Tiles(BaseSettings):
model_config = SettingsConfigDict(env_prefix="treetrail_tiles_")
baseDir: str = "/var/lib/treetrail/mbtiles_files" baseDir: str = "/var/lib/treetrail/mbtiles_files"
useRequestUrl: bool = True useRequestUrl: bool = True
spriteBaseDir: str = "/var/lib/treetrail/mbtiles_sprites" spriteBaseDir: str = "/var/lib/treetrail/mbtiles_sprites"
spriteUrl: str = "/tiles/sprite/sprite" spriteUrl: str = "tiles/sprite/sprite"
spriteBaseUrl: str = "https://treetrail.example.org" spriteBaseUrl: str = "https://treetrail.example.org"
osmBaseDir: str = "/var/lib/treetrail/osm" osmBaseDir: str = "/var/lib/treetrail/osm"
class Map(MyBaseSettings): class Map(BaseSettings):
model_config = SettingsConfigDict(env_prefix="treetrail_map_")
zoom: float = 14.0 zoom: float = 14.0
pitch: float = 0.0 pitch: float = 0.0
lat: float = 12.0000 lat: float = 45.8822
lng: float = 79.8106 lng: float = 6.1781
bearing: float = 0 bearing: float = 0
background: str = "OpenFreeMap" background: str = "OpenFreeMap"
class Geo(MyBaseSettings): class Geo(BaseSettings):
model_config = SettingsConfigDict(env_prefix="treetrail_geo_")
simplify_geom_factor: int = 10000000 simplify_geom_factor: int = 10000000
simplify_preserve_topology: bool = False simplify_preserve_topology: bool = False
class Security(MyBaseSettings): class Security(BaseSettings):
model_config = SettingsConfigDict(env_prefix="treetrail_security_")
""" """
JWT security configuration JWT security configuration
""" """
secret_key: str = token_hex(32) secret_key: str = token_hex(32)
'''Generate with eg.: "openssl rand -hex 32"''' '''Generate with eg.: "openssl rand -hex 32"'''
access_token_expire_minutes: float = 30 access_token_expire_minutes: float = 30
class ExternalMapStyle(MyBaseSettings): class ExternalMapStyle(BaseSettings):
model_config = SettingsConfigDict(env_prefix="treetrail_external_map_style_")
name: str name: str
url: str url: str
class Config(MyBaseSettings): class Config(BaseSettings):
model_config = SettingsConfigDict(env_prefix="treetrail_")
@classmethod @classmethod
def settings_customise_sources( def settings_customise_sources(
@ -155,13 +157,15 @@ class Config(MyBaseSettings):
# postgres: dict # postgres: dict
storage: Storage = Storage() storage: Storage = Storage()
map: Map = Map() map: Map = Map()
mapStyles: dict[str, str] = {} mapStyles: dict[str, str] = {
"OpenFreeMap": "https://tiles.openfreemap.org/styles/liberty"
}
tiles: Tiles = Tiles() tiles: Tiles = Tiles()
security: Security = Security() security: Security = Security()
geo: Geo = Geo() geo: Geo = Geo()
version: str version: str = "-"
db: DB = DB() db: DB = DB()
base_href: str = '/treetrail' base_href: str = "/treetrail"
conf = Config(version=__version__) # type: ignore conf = Config()

View file

@ -14,7 +14,7 @@ from treetrail.config import conf
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
CREATE_DB_TIMEOUT = 30 CREATE_DB_TIMEOUT = 10
engine = create_async_engine( engine = create_async_engine(
conf.db.get_sqla_url(), conf.db.get_sqla_url(),
@ -33,6 +33,7 @@ async def create_db(drop=False):
await conn.run_sync(SQLModel.metadata.drop_all) await conn.run_sync(SQLModel.metadata.drop_all)
await conn.run_sync(SQLModel.metadata.create_all) await conn.run_sync(SQLModel.metadata.create_all)
logger.debug(f"Connect to database with config: {conf.db}")
while attempts > 0: while attempts > 0:
try: try:
await try_once() await try_once()
@ -67,6 +68,7 @@ async def populate_init_db():
"""Populate the database for a fresh install""" """Populate the database for a fresh install"""
from sqlalchemy import text from sqlalchemy import text
from treetrail.security import create_user, add_role, add_user_role from treetrail.security import create_user, add_role, add_user_role
logger.info("Populating initial database") logger.info("Populating initial database")
user = await create_user(username="admin", password="admin") user = await create_user(username="admin", password="admin")
@ -75,7 +77,7 @@ async def populate_init_db():
async with db_session() as session: async with db_session() as session:
for initial in initials: for initial in initials:
await session.execute(text(initial)) await session.execute(text(initial))
logger.debug(f'Added map style {initial}') logger.debug(f"Added map style {initial}")
await session.commit() await session.commit()

View file

@ -19,7 +19,7 @@ map:
baseDir: /var/lib/treetrail/mbtiles_files baseDir: /var/lib/treetrail/mbtiles_files
useRequestUrl: true useRequestUrl: true
spriteBaseDir: /var/lib/treetrail/mbtiles_sprites spriteBaseDir: /var/lib/treetrail/mbtiles_sprites
spriteUrl: /tiles/sprite/sprite spriteUrl: tiles/sprite/sprite
spriteBaseUrl: https://treetrail.example.org spriteBaseUrl: https://treetrail.example.org
osmBaseDir: /var/lib/treetrail/osm osmBaseDir: /var/lib/treetrail/osm
zoom: 14 zoom: 14

View file

@ -141,7 +141,7 @@ class MBTiles:
'tilejson': '2.0.0', 'tilejson': '2.0.0',
'version': 8, 'version': 8,
'glyphs': "/assets/fonts/glyphs/{fontstack}/{range}.pbf", 'glyphs': "/assets/fonts/glyphs/{fontstack}/{range}.pbf",
'sprite': f"{base_url}{conf.tiles.spriteUrl}", 'sprite': f"{conf.tiles.spriteUrl}",
'sources': { 'sources': {
'treeTrailTiles': { 'treeTrailTiles': {
'type': 'vector', 'type': 'vector',

View file

@ -7,19 +7,19 @@ import pandas as pd
from sqlalchemy.ext.declarative import DeclarativeMeta from sqlalchemy.ext.declarative import DeclarativeMeta
from sqlalchemy.engine.row import Row from sqlalchemy.engine.row import Row
from sqlalchemy.sql.selectable import Select from sqlalchemy.sql.selectable import Select
import geopandas as gpd # type: ignore import geopandas as gpd # type: ignore
from treetrail.config import conf from treetrail.config import conf
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class AlchemyEncoder(json.JSONEncoder): class AlchemyEncoder(json.JSONEncoder):
def default(self, obj): def default(self, obj):
if isinstance(obj.__class__, DeclarativeMeta): if isinstance(obj.__class__, DeclarativeMeta):
# an SQLAlchemy class # an SQLAlchemy class
fields = {} fields = {}
for field in [x for x in dir(obj) for field in [x for x in dir(obj) if not x.startswith("_") and x != "metadata"]:
if not x.startswith('_') and x != 'metadata']:
data = obj.__getattribute__(field) data = obj.__getattribute__(field)
try: try:
# this will fail on non-encodable values, like other classes # this will fail on non-encodable values, like other classes
@ -50,24 +50,30 @@ def get_attachment_root(type: str):
def get_attachment_tree_root(): def get_attachment_tree_root():
return get_attachment_root('tree') return get_attachment_root("tree")
def get_attachment_trail_root(): def get_attachment_trail_root():
return get_attachment_root('trail') return get_attachment_root("trail")
def get_attachment_poi_root(): def get_attachment_poi_root():
return get_attachment_root('poi') return get_attachment_root("poi")
def pandas_query(session, query): def pandas_query(session, query):
return pd.read_sql_query(query, session.connection()) return pd.read_sql_query(query, session.connection())
def geopandas_query(session, query: Select, model, *,
# simplify_tolerance: float|None=None, def geopandas_query(
crs=None, cast=True, session,
): query: Select,
model,
*,
# simplify_tolerance: float|None=None,
crs=None,
cast=True,
):
## XXX: I could not get the add_columns work without creating a subquery, ## XXX: I could not get the add_columns work without creating a subquery,
## so moving the simplification to geopandas - see in _get_df ## so moving the simplification to geopandas - see in _get_df
# if simplify_tolerance is not None: # if simplify_tolerance is not None:
@ -78,9 +84,11 @@ def geopandas_query(session, query: Select, model, *,
# query = query.add_columns(new_column) # query = query.add_columns(new_column)
return gpd.GeoDataFrame.from_postgis(query, session.connection(), crs=crs) return gpd.GeoDataFrame.from_postgis(query, session.connection(), crs=crs)
def mkdir(dir: Path | str) -> Path: def mkdir(dir: Path | str) -> Path:
path = Path(dir) path = Path(dir)
if not path.is_dir(): if not path.is_dir():
logger.info(f'Create directory {path}') logger.info(f"Create directory {path}")
path.mkdir(parents=True, exist_ok=True) path.mkdir(parents=True, exist_ok=True)
return path return path

View file

122
treetrail-kube.yaml Normal file
View file

@ -0,0 +1,122 @@
apiVersion: v1
kind: Service
metadata:
name: treetrail
annotations:
io.kubernetes.cri-o.SandboxID/gisaf-backend: treetrail-cri-o
io.kubernetes.cri-o.SandboxID/gisaf-database: treetrail-cri-o
io.kubernetes.cri-o.SandboxID/gisaf-frontend: treetrail-cri-o
io.kubernetes.cri-o.SandboxID/gisaf-redis: treetrail-cri-o
io.podman.annotations.infra.name: treetrail-infra
labels:
app: treetrail
spec:
ports:
- name: "80"
nodePort: 31080
port: 80
targetPort: 80
- name: "4532"
nodePort: 31432
port: 4532
targetPort: 4532
selector:
app: treetrail
type: NodePort
---
apiVersion: v1
kind: Pod
metadata:
labels:
app: treetrail
name: treetrail
spec:
containers:
- image: code.philo.ydns.eu/philorg/treetrail-frontend:latest
args:
- nginx
- -g
- daemon off;
name: treetrail-frontend
ports:
- containerPort: 80
hostPort: 8080
- image: code.philo.ydns.eu/philorg/treetrail-backend:latest
name: treetrail-backend
- image: code.philo.ydns.eu/philorg/treetrail-database:latest
args:
- postgres
name: treetrail-database
ports:
- containerPort: 4532
hostPort: 15432
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: treetrail-pgdata
volumes:
- name: treetrail-pgdata
persistentVolumeClaim:
claimName: treetrail-pgdata-pvc
restartPolicy: Always
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: treetrail-pgdata-pv
labels:
type: local
app: postgres
spec:
storageClassName: manual
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
hostPath:
path: /data/postgresql
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: treetrail-pgdata-pvc
spec:
storageClassName: manual
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: treetrail
namespace: default
#annotations:
# kubernetes.io/ingress.class: traefik
# #traefik.ingress.kubernetes.io/router.middlewares: default-strip-prefix@kubernetescrd
spec:
rules:
- http:
paths:
- path: /treetrail
pathType: Prefix
backend:
service:
name: treetrail-frontend
port:
number: 80
- path: /treetrail/v1
pathType: Prefix
backend:
service:
name: treetrail-backend
port:
number: 8081
- path: /treetrail/plantekey
pathType: Prefix
backend:
service:
name: treetrail-backend
port:
number: 8081

363
uv.lock generated
View file

@ -1,4 +1,5 @@
version = 1 version = 1
revision = 1
requires-python = ">=3.11" requires-python = ">=3.11"
resolution-markers = [ resolution-markers = [
"python_full_version < '3.12'", "python_full_version < '3.12'",
@ -146,6 +147,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d", size = 90377 }, { url = "https://files.pythonhosted.org/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d", size = 90377 },
] ]
[[package]]
name = "asttokens"
version = "2.4.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "six" },
]
sdist = { url = "https://files.pythonhosted.org/packages/45/1d/f03bcb60c4a3212e15f99a56085d93093a497718adf828d050b9d675da81/asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0", size = 62284 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/45/86/4736ac618d82a20d87d2f92ae19441ebc7ac9e7a581d7e58bbe79233b24a/asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", size = 27764 },
]
[[package]] [[package]]
name = "asyncpg" name = "asyncpg"
version = "0.30.0" version = "0.30.0"
@ -330,7 +343,7 @@ name = "click"
version = "8.1.7" version = "8.1.7"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "colorama", marker = "platform_system == 'Windows'" }, { name = "colorama", marker = "sys_platform == 'win32'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 }
wheels = [ wheels = [
@ -375,6 +388,27 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/87/5c/3dab83cc4aba1f4b0e733e3f0c3e7d4386440d660ba5b1e3ff995feb734d/cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", size = 3068026 }, { url = "https://files.pythonhosted.org/packages/87/5c/3dab83cc4aba1f4b0e733e3f0c3e7d4386440d660ba5b1e3ff995feb734d/cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", size = 3068026 },
] ]
[[package]]
name = "decorator"
version = "5.1.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/66/0c/8d907af351aa16b42caae42f9d6aa37b900c67308052d10fdce809f8d952/decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", size = 35016 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 },
]
[[package]]
name = "dunamai"
version = "1.23.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "packaging" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/4e/a5c8c337a1d9ac0384298ade02d322741fb5998041a5ea74d1cd2a4a1d47/dunamai-1.23.0.tar.gz", hash = "sha256:a163746de7ea5acb6dacdab3a6ad621ebc612ed1e528aaa8beedb8887fccd2c4", size = 44681 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/21/4c/963169386309fec4f96fd61210ac0a0666887d0fb0a50205395674d20b71/dunamai-1.23.0-py3-none-any.whl", hash = "sha256:a0906d876e92441793c6a423e16a4802752e723e9c9a5aabdc5535df02dbe041", size = 26342 },
]
[[package]] [[package]]
name = "ecdsa" name = "ecdsa"
version = "0.19.0" version = "0.19.0"
@ -387,6 +421,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/00/e7/ed3243b30d1bec41675b6394a1daae46349dc2b855cb83be846a5a918238/ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a", size = 149266 }, { url = "https://files.pythonhosted.org/packages/00/e7/ed3243b30d1bec41675b6394a1daae46349dc2b855cb83be846a5a918238/ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a", size = 149266 },
] ]
[[package]]
name = "executing"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8c/e3/7d45f492c2c4a0e8e0fad57d081a7c8a0286cdd86372b070cca1ec0caa1e/executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab", size = 977485 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b5/fd/afcd0496feca3276f509df3dbd5dae726fcc756f1a08d9e25abe1733f962/executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf", size = 25805 },
]
[[package]] [[package]]
name = "fastapi" name = "fastapi"
version = "0.115.3" version = "0.115.3"
@ -612,6 +655,40 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
] ]
[[package]]
name = "ipdb"
version = "0.13.13"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "decorator" },
{ name = "ipython" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3d/1b/7e07e7b752017f7693a0f4d41c13e5ca29ce8cbcfdcc1fd6c4ad8c0a27a0/ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726", size = 17042 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/4c/b075da0092003d9a55cf2ecc1cae9384a1ca4f650d51b00fc59875fe76f6/ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4", size = 12130 },
]
[[package]]
name = "ipython"
version = "8.28.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "decorator" },
{ name = "jedi" },
{ name = "matplotlib-inline" },
{ name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
{ name = "prompt-toolkit" },
{ name = "pygments" },
{ name = "stack-data" },
{ name = "traitlets" },
{ name = "typing-extensions", marker = "python_full_version < '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f7/21/48db7d9dd622b9692575004c7c98f85f5629428f58596c59606d36c51b58/ipython-8.28.0.tar.gz", hash = "sha256:0d0d15ca1e01faeb868ef56bc7ee5a0de5bd66885735682e8a322ae289a13d1a", size = 5495762 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f4/3a/5d8680279ada9571de8469220069d27024ee47624af534e537c9ff49a450/ipython-8.28.0-py3-none-any.whl", hash = "sha256:530ef1e7bb693724d3cdc37287c80b07ad9b25986c007a53aa1857272dac3f35", size = 819456 },
]
[[package]] [[package]]
name = "itsdangerous" name = "itsdangerous"
version = "2.2.0" version = "2.2.0"
@ -621,6 +698,30 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234 }, { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234 },
] ]
[[package]]
name = "jedi"
version = "0.19.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "parso" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d6/99/99b493cec4bf43176b678de30f81ed003fd6a647a301b9c927280c600f0a/jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd", size = 1227821 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/20/9f/bc63f0f0737ad7a60800bfd472a4836661adae21f9c2535f3957b1e54ceb/jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0", size = 1569361 },
]
[[package]]
name = "matplotlib-inline"
version = "0.1.7"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "traitlets" },
]
sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 },
]
[[package]] [[package]]
name = "multidict" name = "multidict"
version = "6.1.0" version = "6.1.0"
@ -806,6 +907,28 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 },
] ]
[[package]]
name = "pandas-stubs"
version = "2.2.3.241009"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy" },
{ name = "types-pytz" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d4/30/1ca31098512cdcfbc6ce366072848dff497880d4285281606b5895244bbc/pandas_stubs-2.2.3.241009.tar.gz", hash = "sha256:d4ab618253f0acf78a5d0d2bfd6dffdd92d91a56a69bdc8144e5a5c6d25be3b5", size = 103801 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a3/be/d9ba3109c4c19a78e125f63074c4e436e447f30ece15f0ef1865e7178233/pandas_stubs-2.2.3.241009-py3-none-any.whl", hash = "sha256:3a6f8f142105a42550be677ba741ba532621f4e0acad2155c0e7b2450f114cfa", size = 157883 },
]
[[package]]
name = "parso"
version = "0.8.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 },
]
[[package]] [[package]]
name = "passlib" name = "passlib"
version = "1.7.4" version = "1.7.4"
@ -820,6 +943,18 @@ bcrypt = [
{ name = "bcrypt" }, { name = "bcrypt" },
] ]
[[package]]
name = "pexpect"
version = "4.9.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "ptyprocess" },
]
sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 },
]
[[package]] [[package]]
name = "pillow" name = "pillow"
version = "11.0.0" version = "11.0.0"
@ -878,6 +1013,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
] ]
[[package]]
name = "prompt-toolkit"
version = "3.0.48"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "wcwidth" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2d/4f/feb5e137aff82f7c7f3248267b97451da3644f6cdc218edfe549fb354127/prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90", size = 424684 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a9/6a/fd08d94654f7e67c52ca30523a178b3f8ccc4237fce4be90d39c938a831a/prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e", size = 386595 },
]
[[package]] [[package]]
name = "propcache" name = "propcache"
version = "0.2.0" version = "0.2.0"
@ -975,31 +1122,60 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712 }, { url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712 },
{ url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155 }, { url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155 },
{ url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356 }, { url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356 },
{ url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224 },
]
[[package]]
name = "ptyprocess"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 },
]
[[package]]
name = "pure-eval"
version = "0.2.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 },
] ]
[[package]] [[package]]
name = "pyarrow" name = "pyarrow"
version = "17.0.0" version = "19.0.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ sdist = { url = "https://files.pythonhosted.org/packages/7f/09/a9046344212690f0632b9c709f9bf18506522feb333c894d0de81d62341a/pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e", size = 1129437 }
{ name = "numpy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/27/4e/ea6d43f324169f8aec0e57569443a38bab4b398d09769ca64f7b4d467de3/pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28", size = 1112479 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/f9/46/ce89f87c2936f5bb9d879473b9663ce7a4b1f4359acc2f0eb39865eaa1af/pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977", size = 29028748 }, { url = "https://files.pythonhosted.org/packages/a0/55/f1a8d838ec07fe3ca53edbe76f782df7b9aafd4417080eebf0b42aab0c52/pyarrow-19.0.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:cc55d71898ea30dc95900297d191377caba257612f384207fe9f8293b5850f90", size = 30713987 },
{ url = "https://files.pythonhosted.org/packages/8d/8e/ce2e9b2146de422f6638333c01903140e9ada244a2a477918a368306c64c/pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3", size = 27190965 }, { url = "https://files.pythonhosted.org/packages/13/12/428861540bb54c98a140ae858a11f71d041ef9e501e6b7eb965ca7909505/pyarrow-19.0.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:7a544ec12de66769612b2d6988c36adc96fb9767ecc8ee0a4d270b10b1c51e00", size = 32135613 },
{ url = "https://files.pythonhosted.org/packages/3b/c8/5675719570eb1acd809481c6d64e2136ffb340bc387f4ca62dce79516cea/pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15", size = 39269081 }, { url = "https://files.pythonhosted.org/packages/2f/8a/23d7cc5ae2066c6c736bce1db8ea7bc9ac3ef97ac7e1c1667706c764d2d9/pyarrow-19.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0148bb4fc158bfbc3d6dfe5001d93ebeed253793fff4435167f6ce1dc4bddeae", size = 41149147 },
{ url = "https://files.pythonhosted.org/packages/5e/78/3931194f16ab681ebb87ad252e7b8d2c8b23dad49706cadc865dff4a1dd3/pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597", size = 39864921 }, { url = "https://files.pythonhosted.org/packages/a2/7a/845d151bb81a892dfb368bf11db584cf8b216963ccce40a5cf50a2492a18/pyarrow-19.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f24faab6ed18f216a37870d8c5623f9c044566d75ec586ef884e13a02a9d62c5", size = 42178045 },
{ url = "https://files.pythonhosted.org/packages/d8/81/69b6606093363f55a2a574c018901c40952d4e902e670656d18213c71ad7/pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420", size = 38740798 }, { url = "https://files.pythonhosted.org/packages/a7/31/e7282d79a70816132cf6cae7e378adfccce9ae10352d21c2fecf9d9756dd/pyarrow-19.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4982f8e2b7afd6dae8608d70ba5bd91699077323f812a0448d8b7abdff6cb5d3", size = 40532998 },
{ url = "https://files.pythonhosted.org/packages/4c/21/9ca93b84b92ef927814cb7ba37f0774a484c849d58f0b692b16af8eebcfb/pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4", size = 39871877 }, { url = "https://files.pythonhosted.org/packages/b8/82/20f3c290d6e705e2ee9c1fa1d5a0869365ee477e1788073d8b548da8b64c/pyarrow-19.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:49a3aecb62c1be1d822f8bf629226d4a96418228a42f5b40835c1f10d42e4db6", size = 42084055 },
{ url = "https://files.pythonhosted.org/packages/30/d1/63a7c248432c71c7d3ee803e706590a0b81ce1a8d2b2ae49677774b813bb/pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03", size = 25151089 }, { url = "https://files.pythonhosted.org/packages/ff/77/e62aebd343238863f2c9f080ad2ef6ace25c919c6ab383436b5b81cbeef7/pyarrow-19.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:008a4009efdb4ea3d2e18f05cd31f9d43c388aad29c636112c2966605ba33466", size = 25283133 },
{ url = "https://files.pythonhosted.org/packages/d4/62/ce6ac1275a432b4a27c55fe96c58147f111d8ba1ad800a112d31859fae2f/pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22", size = 29019418 }, { url = "https://files.pythonhosted.org/packages/78/b4/94e828704b050e723f67d67c3535cf7076c7432cd4cf046e4bb3b96a9c9d/pyarrow-19.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:80b2ad2b193e7d19e81008a96e313fbd53157945c7be9ac65f44f8937a55427b", size = 30670749 },
{ url = "https://files.pythonhosted.org/packages/8e/0a/dbd0c134e7a0c30bea439675cc120012337202e5fac7163ba839aa3691d2/pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053", size = 27152197 }, { url = "https://files.pythonhosted.org/packages/7e/3b/4692965e04bb1df55e2c314c4296f1eb12b4f3052d4cf43d29e076aedf66/pyarrow-19.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ee8dec072569f43835932a3b10c55973593abc00936c202707a4ad06af7cb294", size = 32128007 },
{ url = "https://files.pythonhosted.org/packages/cb/05/3f4a16498349db79090767620d6dc23c1ec0c658a668d61d76b87706c65d/pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a", size = 39263026 }, { url = "https://files.pythonhosted.org/packages/22/f7/2239af706252c6582a5635c35caa17cb4d401cd74a87821ef702e3888957/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5d1ec7ec5324b98887bdc006f4d2ce534e10e60f7ad995e7875ffa0ff9cb14", size = 41144566 },
{ url = "https://files.pythonhosted.org/packages/c2/0c/ea2107236740be8fa0e0d4a293a095c9f43546a2465bb7df34eee9126b09/pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc", size = 39880798 }, { url = "https://files.pythonhosted.org/packages/fb/e3/c9661b2b2849cfefddd9fd65b64e093594b231b472de08ff658f76c732b2/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ad4c0eb4e2a9aeb990af6c09e6fa0b195c8c0e7b272ecc8d4d2b6574809d34", size = 42202991 },
{ url = "https://files.pythonhosted.org/packages/f6/b0/b9164a8bc495083c10c281cc65064553ec87b7537d6f742a89d5953a2a3e/pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a", size = 38715172 }, { url = "https://files.pythonhosted.org/packages/fe/4f/a2c0ed309167ef436674782dfee4a124570ba64299c551e38d3fdaf0a17b/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d383591f3dcbe545f6cc62daaef9c7cdfe0dff0fb9e1c8121101cabe9098cfa6", size = 40507986 },
{ url = "https://files.pythonhosted.org/packages/f1/c4/9625418a1413005e486c006e56675334929fad864347c5ae7c1b2e7fe639/pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b", size = 39874508 }, { url = "https://files.pythonhosted.org/packages/27/2e/29bb28a7102a6f71026a9d70d1d61df926887e36ec797f2e6acfd2dd3867/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b4c4156a625f1e35d6c0b2132635a237708944eb41df5fbe7d50f20d20c17832", size = 42087026 },
{ url = "https://files.pythonhosted.org/packages/ae/49/baafe2a964f663413be3bd1cf5c45ed98c5e42e804e2328e18f4570027c1/pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7", size = 25099235 }, { url = "https://files.pythonhosted.org/packages/16/33/2a67c0f783251106aeeee516f4806161e7b481f7d744d0d643d2f30230a5/pyarrow-19.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960", size = 25250108 },
{ url = "https://files.pythonhosted.org/packages/2b/8d/275c58d4b00781bd36579501a259eacc5c6dfb369be4ddeb672ceb551d2d/pyarrow-19.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e45274b20e524ae5c39d7fc1ca2aa923aab494776d2d4b316b49ec7572ca324c", size = 30653552 },
{ url = "https://files.pythonhosted.org/packages/a0/9e/e6aca5cc4ef0c7aec5f8db93feb0bde08dbad8c56b9014216205d271101b/pyarrow-19.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d9dedeaf19097a143ed6da37f04f4051aba353c95ef507764d344229b2b740ae", size = 32103413 },
{ url = "https://files.pythonhosted.org/packages/6a/fa/a7033f66e5d4f1308c7eb0dfcd2ccd70f881724eb6fd1776657fdf65458f/pyarrow-19.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ebfb5171bb5f4a52319344ebbbecc731af3f021e49318c74f33d520d31ae0c4", size = 41134869 },
{ url = "https://files.pythonhosted.org/packages/2d/92/34d2569be8e7abdc9d145c98dc410db0071ac579b92ebc30da35f500d630/pyarrow-19.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a21d39fbdb948857f67eacb5bbaaf36802de044ec36fbef7a1c8f0dd3a4ab2", size = 42192626 },
{ url = "https://files.pythonhosted.org/packages/0a/1f/80c617b1084fc833804dc3309aa9d8daacd46f9ec8d736df733f15aebe2c/pyarrow-19.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:99bc1bec6d234359743b01e70d4310d0ab240c3d6b0da7e2a93663b0158616f6", size = 40496708 },
{ url = "https://files.pythonhosted.org/packages/e6/90/83698fcecf939a611c8d9a78e38e7fed7792dcc4317e29e72cf8135526fb/pyarrow-19.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1b93ef2c93e77c442c979b0d596af45e4665d8b96da598db145b0fec014b9136", size = 42075728 },
{ url = "https://files.pythonhosted.org/packages/40/49/2325f5c9e7a1c125c01ba0c509d400b152c972a47958768e4e35e04d13d8/pyarrow-19.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:d9d46e06846a41ba906ab25302cf0fd522f81aa2a85a71021826f34639ad31ef", size = 25242568 },
{ url = "https://files.pythonhosted.org/packages/3f/72/135088d995a759d4d916ec4824cb19e066585b4909ebad4ab196177aa825/pyarrow-19.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c0fe3dbbf054a00d1f162fda94ce236a899ca01123a798c561ba307ca38af5f0", size = 30702371 },
{ url = "https://files.pythonhosted.org/packages/2e/01/00beeebd33d6bac701f20816a29d2018eba463616bbc07397fdf99ac4ce3/pyarrow-19.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:96606c3ba57944d128e8a8399da4812f56c7f61de8c647e3470b417f795d0ef9", size = 32116046 },
{ url = "https://files.pythonhosted.org/packages/1f/c9/23b1ea718dfe967cbd986d16cf2a31fe59d015874258baae16d7ea0ccabc/pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f04d49a6b64cf24719c080b3c2029a3a5b16417fd5fd7c4041f94233af732f3", size = 41091183 },
{ url = "https://files.pythonhosted.org/packages/3a/d4/b4a3aa781a2c715520aa8ab4fe2e7fa49d33a1d4e71c8fc6ab7b5de7a3f8/pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9137cf7e1640dce4c190551ee69d478f7121b5c6f323553b319cac936395f6", size = 42171896 },
{ url = "https://files.pythonhosted.org/packages/23/1b/716d4cd5a3cbc387c6e6745d2704c4b46654ba2668260d25c402626c5ddb/pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:7c1bca1897c28013db5e4c83944a2ab53231f541b9e0c3f4791206d0c0de389a", size = 40464851 },
{ url = "https://files.pythonhosted.org/packages/ed/bd/54907846383dcc7ee28772d7e646f6c34276a17da740002a5cefe90f04f7/pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:58d9397b2e273ef76264b45531e9d552d8ec8a6688b7390b5be44c02a37aade8", size = 42085744 },
] ]
[[package]] [[package]]
@ -1094,6 +1270,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/34/19/26bb6bdb9fdad5f0dfce538780814084fb667b4bc37fcb28459c14b8d3b5/pydantic_settings-2.6.0-py3-none-any.whl", hash = "sha256:4a819166f119b74d7f8c765196b165f95cc7487ce58ea27dec8a5a26be0970e0", size = 28578 }, { url = "https://files.pythonhosted.org/packages/34/19/26bb6bdb9fdad5f0dfce538780814084fb667b4bc37fcb28459c14b8d3b5/pydantic_settings-2.6.0-py3-none-any.whl", hash = "sha256:4a819166f119b74d7f8c765196b165f95cc7487ce58ea27dec8a5a26be0970e0", size = 28578 },
] ]
[[package]]
name = "pygments"
version = "2.18.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 },
]
[[package]] [[package]]
name = "pyogrio" name = "pyogrio"
version = "0.10.0" version = "0.10.0"
@ -1391,6 +1576,20 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276 }, { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276 },
] ]
[[package]]
name = "stack-data"
version = "0.6.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asttokens" },
{ name = "executing" },
{ name = "pure-eval" },
]
sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 },
]
[[package]] [[package]]
name = "starlette" name = "starlette"
version = "0.41.0" version = "0.41.0"
@ -1404,8 +1603,16 @@ wheels = [
] ]
[[package]] [[package]]
name = "treetrail-srv" name = "traitlets"
version = "0.1.0" version = "5.14.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 },
]
[[package]]
name = "treetrail-backend"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "aiofiles" }, { name = "aiofiles" },
@ -1434,8 +1641,17 @@ dependencies = [
[package.dev-dependencies] [package.dev-dependencies]
dev = [ dev = [
{ name = "dunamai" },
{ name = "httpx" }, { name = "httpx" },
{ name = "ipdb" },
{ name = "pandas-stubs" },
{ name = "pytest" }, { name = "pytest" },
{ name = "types-aiofiles" },
{ name = "types-passlib" },
{ name = "types-pillow" },
{ name = "types-python-jose" },
{ name = "types-pyyaml" },
{ name = "types-requests" },
] ]
[package.metadata] [package.metadata]
@ -1453,7 +1669,7 @@ requires-dist = [
{ name = "passlib", extras = ["bcrypt"] }, { name = "passlib", extras = ["bcrypt"] },
{ name = "pillow" }, { name = "pillow" },
{ name = "psycopg2-binary" }, { name = "psycopg2-binary" },
{ name = "pyarrow" }, { name = "pyarrow", specifier = ">=19.0.1" },
{ name = "pydantic-settings" }, { name = "pydantic-settings" },
{ name = "python-jose", extras = ["cryptography"] }, { name = "python-jose", extras = ["cryptography"] },
{ name = "python-multipart" }, { name = "python-multipart" },
@ -1466,8 +1682,96 @@ requires-dist = [
[package.metadata.requires-dev] [package.metadata.requires-dev]
dev = [ dev = [
{ name = "httpx", specifier = ">=0.27.2" }, { name = "dunamai", specifier = ">=1.23.0" },
{ name = "pytest", specifier = ">=8.3.3" }, { name = "httpx" },
{ name = "ipdb" },
{ name = "ipdb", specifier = ">=0.13.13" },
{ name = "pandas-stubs" },
{ name = "pytest" },
{ name = "types-aiofiles" },
{ name = "types-passlib" },
{ name = "types-pillow" },
{ name = "types-python-jose" },
{ name = "types-pyyaml" },
{ name = "types-requests" },
]
[[package]]
name = "types-aiofiles"
version = "24.1.0.20240626"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/13/e9/013940b017c313c2e15c64017268fdb0c25e0638621fb8a5d9ebe00fb0f4/types-aiofiles-24.1.0.20240626.tar.gz", hash = "sha256:48604663e24bc2d5038eac05ccc33e75799b0779e93e13d6a8f711ddc306ac08", size = 9357 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c3/ad/c4b3275d21c5be79487c4f6ed7cd13336997746fe099236cb29256a44a90/types_aiofiles-24.1.0.20240626-py3-none-any.whl", hash = "sha256:7939eca4a8b4f9c6491b6e8ef160caee9a21d32e18534a57d5ed90aee47c66b4", size = 9389 },
]
[[package]]
name = "types-passlib"
version = "1.7.7.20240819"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d3/19/5041c4bce2909c67fc3f9471ad67972d94c31cb591a970a8faf1220a3717/types-passlib-1.7.7.20240819.tar.gz", hash = "sha256:8fc8df71623845032293d5cf7f8091f0adfeba02d387a2888684b8413f14b3d0", size = 18386 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f1/4b/606ac25e89908e4577cd1aa19ffbebe55a6720cff69303db68701f3cc388/types_passlib-1.7.7.20240819-py3-none-any.whl", hash = "sha256:c4d299083497b66e12258c7b77c08952574213fdf7009da3135d8181a6a25f23", size = 33240 },
]
[[package]]
name = "types-pillow"
version = "10.2.0.20240822"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/18/4a/4495264dddaa600d65d68bcedb64dcccf9d9da61adff51f7d2ffd8e4c9ce/types-Pillow-10.2.0.20240822.tar.gz", hash = "sha256:559fb52a2ef991c326e4a0d20accb3bb63a7ba8d40eb493e0ecb0310ba52f0d3", size = 35389 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/66/23/e81a5354859831fcf54d488d33b80ba6133ea84f874a9c0ec40a4881e133/types_Pillow-10.2.0.20240822-py3-none-any.whl", hash = "sha256:d9dab025aba07aeb12fd50a6799d4eac52a9603488eca09d7662543983f16c5d", size = 54354 },
]
[[package]]
name = "types-pyasn1"
version = "0.6.0.20240913"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c5/e2/42410b64ba53584a8f2b681e76b23569a61869a22325cfeef2728e999ffd/types-pyasn1-0.6.0.20240913.tar.gz", hash = "sha256:a1da054db13d3d4ccfa69c515678154014336ad3d9f9ade01845f9edb1a2bc71", size = 12375 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ff/6a/847b2a137dba37974e17cc2ae5f62bed0d68006ac609a063810038adecff/types_pyasn1-0.6.0.20240913-py3-none-any.whl", hash = "sha256:95f3cb1fbd63ff91cd0410945f8aeae6b0be359533c00f39d8e17124884157af", size = 19338 },
]
[[package]]
name = "types-python-jose"
version = "3.3.4.20240106"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "types-pyasn1" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4d/83/5824277f62a0a07ab3eaade216f19b59fadea4efdad9071d70799d97f170/types-python-jose-3.3.4.20240106.tar.gz", hash = "sha256:b18cf8c5080bbfe1ef7c3b707986435d9efca3e90889acb6a06f65e06bc3405a", size = 6937 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/84/bd/fe23e814f1ca70f8427bf5defb981b8e0636731863c7836a6d6b6e49c715/types_python_jose-3.3.4.20240106-py3-none-any.whl", hash = "sha256:b515a6c0c61f5e2a53bc93e3a2b024cbd42563e2e19cbde9fd1c2cc2cfe77ccc", size = 9712 },
]
[[package]]
name = "types-pytz"
version = "2024.2.0.20241003"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/66/d0/73aa3063a9ef9881bd7103cb4ae379bfd8fafda0e86b01b694d676313a4b/types-pytz-2024.2.0.20241003.tar.gz", hash = "sha256:575dc38f385a922a212bac00a7d6d2e16e141132a3c955078f4a4fd13ed6cb44", size = 5474 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/86/60/2a2977ce0f91255bbb668350b127a801a06ad37c326a2e5bfd52f03e0784/types_pytz-2024.2.0.20241003-py3-none-any.whl", hash = "sha256:3e22df1336c0c6ad1d29163c8fda82736909eb977281cb823c57f8bae07118b7", size = 5245 },
]
[[package]]
name = "types-pyyaml"
version = "6.0.12.20240917"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/92/7d/a95df0a11f95c8f48d7683f03e4aed1a2c0fc73e9de15cca4d38034bea1a/types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587", size = 12381 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9e/2c/c1d81d680997d24b0542aa336f0a65bd7835e5224b7670f33a7d617da379/types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570", size = 15264 },
]
[[package]]
name = "types-requests"
version = "2.32.0.20241016"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fa/3c/4f2a430c01a22abd49a583b6b944173e39e7d01b688190a5618bd59a2e22/types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95", size = 18065 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d7/01/485b3026ff90e5190b5e24f1711522e06c79f4a56c8f4b95848ac072e20f/types_requests-2.32.0.20241016-py3-none-any.whl", hash = "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747", size = 15836 },
] ]
[[package]] [[package]]
@ -1608,6 +1912,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a6/8b/8a7755c5e7221bb35fe4af2dc44db9174f90ebf0344fd5e9b1e8b42d381e/watchfiles-0.24.0-cp313-none-win_amd64.whl", hash = "sha256:a974231b4fdd1bb7f62064a0565a6b107d27d21d9acb50c484d2cdba515b9366", size = 276622 }, { url = "https://files.pythonhosted.org/packages/a6/8b/8a7755c5e7221bb35fe4af2dc44db9174f90ebf0344fd5e9b1e8b42d381e/watchfiles-0.24.0-cp313-none-win_amd64.whl", hash = "sha256:a974231b4fdd1bb7f62064a0565a6b107d27d21d9acb50c484d2cdba515b9366", size = 276622 },
] ]
[[package]]
name = "wcwidth"
version = "0.2.13"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 },
]
[[package]] [[package]]
name = "websockets" name = "websockets"
version = "13.1" version = "13.1"