Compare commits

..

No commits in common. "master" and "0.3.1" have entirely different histories.

28 changed files with 222 additions and 881 deletions

View file

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

View file

@ -0,0 +1,19 @@
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

View file

@ -1,70 +0,0 @@
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

View file

@ -1,21 +0,0 @@
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,17 +1,12 @@
FROM docker.io/library/python:latest FROM localhost/trixie_python
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 .
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/ # Instances should override the prod.yaml file
COPY ./prod.yaml /etc/treetrail/prod.yaml
COPY . /app CMD ["uvicorn", "treetrail.application:app", "--port", "8081", "--log-config", "logging.yaml", "--host", "0.0.0.0"]
# 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

@ -0,0 +1,7 @@
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

@ -0,0 +1,36 @@
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__

View file

@ -1,10 +0,0 @@
# 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

View file

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

View file

@ -1,10 +0,0 @@
# 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

View file

@ -1,30 +0,0 @@
# 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,23 +1,11 @@
# Build: podman build -t code.philo.ydns.eu/philorg/trixie_python -f Containerfile.trixie_python FROM debian:trixie-slim
MAINTAINER philo email phil.dev@philome.mooo.com
FROM docker.io/library/debian:trixie-slim RUN apt update
RUN apt install --no-install-recommends -y python-is-python3 python3-pip python3-venv
RUN pip install --break-system-packages pdm
RUN <<EOT RUN apt-get clean && \
apt-get update -qy rm -rf /var/lib/apt/lists/*
apt-get install -qyy \
-o APT::Install-Recommends=false \
-o APT::Install-Suggests=false \
ca-certificates \
python-is-python3
apt-get clean
EOT
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv RUN rm -rf /root/.cache
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,6 +1,3 @@
*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)

View file

@ -1,93 +0,0 @@
- 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,80 +1,77 @@
[project] [project]
name = "treetrail-backend" name = "treetrail-srv"
dynamic = ["version"] version = "0.3.0"
#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 = [{ name = "Philippe May", email = "phil.treetrail@philome.mooo.com" }] authors = [
{ 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>=19.0.1", "pyarrow",
"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-backend = "treetrail_backend:main" #treetrail-srv = "treetrail_srv:main"
[dependency-groups]
dev = ["dunamai>=1.23.0", "ipdb>=0.13.13"]
[build-system] [build-system]
requires = ["hatchling", "uv-dynamic-versioning"] requires = ["hatchling"]
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.black] #[tool.pdm.version]
line-length = 98 #source = "scm"
#write_to = "treetrail/_version.py"
#write_template = "__version__ = '{}'"
#

View file

@ -1,8 +0,0 @@
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

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

View file

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

View file

@ -36,8 +36,7 @@ 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 from treetrail.config import conf, get_cache_dir, __version__
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
@ -51,7 +50,7 @@ api_app = FastAPI(
default_response_class=responses.ORJSONResponse, default_response_class=responses.ORJSONResponse,
) )
re_findmimetype = re.compile(r'^data:(\S+);') # type: ignore re_findmimetype = re.compile('^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,7 +51,22 @@ 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="Treetrail backend / server", description="fastapi Application 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",
@ -139,11 +154,6 @@ 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:
@ -151,11 +161,6 @@ 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
@ -228,8 +233,7 @@ def _main(argv=None):
sys.exit(0) sys.exit(0)
print( print(
'No CLI option was given, try --help', 'This application needs to be run with an asgi server like uvicorn.',
'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,6 +12,8 @@ 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")
@ -63,11 +65,16 @@ 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):
model_config = SettingsConfigDict(env_prefix="treetrail_db_") class DB(MyBaseSettings):
# uri: str # uri: str
host: str = "localhost" host: str = "treetrail-database"
port: int = 5432 port: int = 5432
user: str = "treetrail" user: str = "treetrail"
db: str = "treetrail" db: str = "treetrail"
@ -85,62 +92,53 @@ class DB(BaseSettings):
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(BaseSettings): class App(MyBaseSettings):
model_config = SettingsConfigDict(env_prefix="treetrail_app_")
title: str = "Tree Trail" title: str = "Tree Trail"
class Storage(BaseSettings): class Storage(MyBaseSettings):
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(BaseSettings): class Tiles(MyBaseSettings):
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(BaseSettings): class Map(MyBaseSettings):
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 = 45.8822 lat: float = 12.0000
lng: float = 6.1781 lng: float = 79.8106
bearing: float = 0 bearing: float = 0
background: str = "OpenFreeMap" background: str = "OpenFreeMap"
class Geo(BaseSettings): class Geo(MyBaseSettings):
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(BaseSettings): class Security(MyBaseSettings):
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(BaseSettings): class ExternalMapStyle(MyBaseSettings):
model_config = SettingsConfigDict(env_prefix="treetrail_external_map_style_")
name: str name: str
url: str url: str
class Config(BaseSettings): class Config(MyBaseSettings):
model_config = SettingsConfigDict(env_prefix="treetrail_")
@classmethod @classmethod
def settings_customise_sources( def settings_customise_sources(
@ -157,15 +155,13 @@ class Config(BaseSettings):
# 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() conf = Config(version=__version__) # type: ignore

View file

@ -14,7 +14,7 @@ from treetrail.config import conf
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
CREATE_DB_TIMEOUT = 10 CREATE_DB_TIMEOUT = 30
engine = create_async_engine( engine = create_async_engine(
conf.db.get_sqla_url(), conf.db.get_sqla_url(),
@ -33,7 +33,6 @@ 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()
@ -68,7 +67,6 @@ 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")
@ -77,7 +75,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"{conf.tiles.spriteUrl}", 'sprite': f"{base_url}{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) if not x.startswith("_") and x != "metadata"]: for field in [x for x in dir(obj)
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,30 +50,24 @@ 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, *,
def geopandas_query( # simplify_tolerance: float|None=None,
session, crs=None, cast=True,
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:
@ -84,11 +78,9 @@ def geopandas_query(
# 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

0
tests/__init__.py Normal file
View file

View file

@ -1,122 +0,0 @@
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,5 +1,4 @@
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'",
@ -147,18 +146,6 @@ 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"
@ -343,7 +330,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 = "sys_platform == 'win32'" }, { name = "colorama", marker = "platform_system == 'Windows'" },
] ]
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 = [
@ -388,27 +375,6 @@ 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"
@ -421,15 +387,6 @@ 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"
@ -655,40 +612,6 @@ 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"
@ -698,30 +621,6 @@ 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"
@ -907,28 +806,6 @@ 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"
@ -943,18 +820,6 @@ 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"
@ -1013,18 +878,6 @@ 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"
@ -1122,60 +975,31 @@ 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 = "19.0.1" version = "17.0.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7f/09/a9046344212690f0632b9c709f9bf18506522feb333c894d0de81d62341a/pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e", size = 1129437 } dependencies = [
{ 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/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/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/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/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/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/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/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/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/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/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/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/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/ff/77/e62aebd343238863f2c9f080ad2ef6ace25c919c6ab383436b5b81cbeef7/pyarrow-19.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:008a4009efdb4ea3d2e18f05cd31f9d43c388aad29c636112c2966605ba33466", size = 25283133 }, { 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/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/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/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/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/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/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/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/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/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/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/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/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/16/33/2a67c0f783251106aeeee516f4806161e7b481f7d744d0d643d2f30230a5/pyarrow-19.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960", size = 25250108 }, { 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/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]]
@ -1270,15 +1094,6 @@ 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"
@ -1576,20 +1391,6 @@ 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"
@ -1603,16 +1404,8 @@ wheels = [
] ]
[[package]] [[package]]
name = "traitlets" name = "treetrail-srv"
version = "5.14.3" version = "0.1.0"
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" },
@ -1641,17 +1434,8 @@ 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]
@ -1669,7 +1453,7 @@ requires-dist = [
{ name = "passlib", extras = ["bcrypt"] }, { name = "passlib", extras = ["bcrypt"] },
{ name = "pillow" }, { name = "pillow" },
{ name = "psycopg2-binary" }, { name = "psycopg2-binary" },
{ name = "pyarrow", specifier = ">=19.0.1" }, { name = "pyarrow" },
{ name = "pydantic-settings" }, { name = "pydantic-settings" },
{ name = "python-jose", extras = ["cryptography"] }, { name = "python-jose", extras = ["cryptography"] },
{ name = "python-multipart" }, { name = "python-multipart" },
@ -1682,96 +1466,8 @@ requires-dist = [
[package.metadata.requires-dev] [package.metadata.requires-dev]
dev = [ dev = [
{ name = "dunamai", specifier = ">=1.23.0" }, { name = "httpx", specifier = ">=0.27.2" },
{ name = "httpx" }, { name = "pytest", specifier = ">=8.3.3" },
{ 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]]
@ -1912,15 +1608,6 @@ 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"