From e8132cd70d4bd7b24f0aaafc60cc1196d9848d44 Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Mon, 29 Jan 2024 09:57:26 +0000 Subject: [PATCH 01/20] Improving backend README --- back/Makefile | 22 +++--- back/README.md | 84 ++++++++++++++++----- back/tests/conftest.py | 42 +++++++---- back/tests/test_database/test_migrations.py | 59 +++++++++++---- back/tests/test_routers/conftest.py | 4 +- 5 files changed, 150 insertions(+), 61 deletions(-) diff --git a/back/Makefile b/back/Makefile index 9338472e..47d9179a 100644 --- a/back/Makefile +++ b/back/Makefile @@ -27,10 +27,10 @@ export PRINT_HELP_PYSCRIPT BROWSER := python -c "$$BROWSER_PYSCRIPT" -help: +help: ## show this help message and exit @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) -clean: clean-build clean-pyc clean-test clean-docs +clean: clean-build clean-pyc clean-test clean-docs ## remove all build, test, coverage and Python artifacts clean-build: rm -fr build/ @@ -73,35 +73,35 @@ lint/pyright: lint: lint/flake8 lint/black lint/pycodestyle lint/pydocstyle lint/pylint lint/pyright ## check style -format: +format: ## format code isort src tests black src tests -test: +test: ## Run all tests pytest -n auto -install: clean +install: clean ## install the package to the active Python's site-packages pip install . -coverage: +coverage: ## check code coverage coverage run --source whombat -m pytest coverage report -m coverage html $(BROWSER) htmlcov/index.html -build-docs: +build-docs: ## build documentation mkdocs build -build-guide: +build-guide: ## build user guide mkdocs build -f mkdocs-guide.yml -d src/whombat/user_guide -serve-docs: +serve-docs: ## serve documentation URL="http://localhost:8000/whombat/"; xdg-open $$URL || sensible-browser $$URL || x-www-browser $$URL || gnome-open $$URL @$(ENV_PREFIX)mkdocs serve -serve-guide: +serve-guide: ## serve user guide URL="http://localhost:8000/whombat/"; xdg-open $$URL || sensible-browser $$URL || x-www-browser $$URL || gnome-open $$URL @$(ENV_PREFIX)mkdocs serve -f mkdocs-guide.yml -serve-dev: +serve-dev: ## serve development backend WHOMBAT_DEV=true python -m whombat diff --git a/back/README.md b/back/README.md index 2e88c515..1f5efd93 100644 --- a/back/README.md +++ b/back/README.md @@ -1,36 +1,84 @@ -# Whombat +# Whombat - Python Backend -> [!TIP] -> Read the latest [documentation](https://mbsantiago.github.io/whombat/) +**whombat** is an open-source web-based audio annotation tool designed to +facilitate audio data labeling and annotation, with a special focus on aiding +machine learning model development. -> [!IMPORTANT] -> Whombat is in beta mode. Most features are implemented but probably buggy. -> Expect many changes before a first release. +For additional details on installing the entire application and its usage, refer +to the main [README](https://github.com/mbsantiago/whombat). -**whombat** is an open-source web-based audio annotation tool designed to facilitate -audio data labeling and annotation, with a special focus on aiding machine -learning model development. +For the latest updates and detailed documentation, check out the official +[documentation](https://mbsantiago.github.io/whombat/). ## Installation -To use whombat, follow these steps: +### With Pip + +The most straightforward method to set up the backend and Whombat Python API is +using pip. Execute the following command: + +```bash +pip install whombat +``` + +### From Source Code Clone the repository: -```shell +```bash git clone https://github.com/mbsantiago/whombat.git ``` -Install the required dependencies: +Install the package: -```shell -bash scripts/install.sh +```bash +cd whombat/backend +pip install . ``` -Run the application: +### With Docker -```shell -bash scripts/run.sh +Run Whombat inside a Docker container. Build the container by cloning the repository and executing: + + +```bash +git clone https://github.com/mbsantiago/whombat.git +docker build -t whombat . ``` -Access whombat in your web browser at . +Once the build is complete, run the container with: + +```bash +docker run -p 5000:5000 whombat +``` + +### Development Environment + +We manage Whombat's development with `pdm`. + +1. Follow the official [installation instructions](https://pdm-project.org/latest/#installation) to get `pdm` on your machine. + +2. Clone the repository: + +```bash +git clone https://github.com/mbsantiago/whombat.git +``` + +3. Navigate to the backend directory and install dependencies: + +```bash +cd whombat/back +pdm install --dev +``` + +4. Start the development server: + +```bash +pdm run make serve-dev +``` + +or + +```bash +WHOMBAT_DEV=true pdm run python -m whombat +``` diff --git a/back/tests/conftest.py b/back/tests/conftest.py index ddb3cb59..39afd7c7 100644 --- a/back/tests/conftest.py +++ b/back/tests/conftest.py @@ -2,6 +2,7 @@ import logging import os import random +import shutil import string from collections.abc import Callable from pathlib import Path @@ -63,23 +64,44 @@ def audio_dir(tmp_path: Path): return path +@pytest.fixture(scope="session") +async def database_template(tmp_path_factory: pytest.TempPathFactory) -> Path: + """Create a database template for testing.""" + tmp_path = tmp_path_factory.mktemp("template") + db_path = tmp_path / "template.db" + settings = Settings( + db_dialect="sqlite", + db_name=str(db_path), + audio_dir=tmp_path / "audio", + ) + await init_database(settings) + return db_path + + @pytest.fixture -def database_test(tmp_path: Path) -> Path: - """Fixture to create a temporary database.""" - return tmp_path / "test.db" +def database_path(tmp_path: Path, database_template: Path) -> Path: + """Create a database for testing. + + Copies the database template to a temporary location. + """ + path = tmp_path / "test.db" + shutil.copy(database_template, path) + return path @pytest.fixture(autouse=True) def settings( audio_dir: Path, - database_test: Path, + database_path: Path, ) -> Settings: """Fixture to return the settings.""" return Settings( db_dialect="sqlite", - db_name=str(database_test.absolute().resolve()), + db_name=str(database_path), audio_dir=audio_dir, open_on_startup=False, + log_to_file=False, + log_to_stdout=True, ) @@ -160,19 +182,9 @@ def wav_factory( return wav_factory -@pytest.fixture -async def database_init( - settings: Settings, -) -> AsyncGenerator[None, None]: - """Create a session that uses an in-memory database.""" - await init_database(settings) - yield - - @pytest.fixture async def session( database_url: URL, - database_init, ) -> AsyncGenerator[AsyncSession, None]: """Create a session that uses an in-memory database.""" async with api.create_session(db_url=database_url) as sess: diff --git a/back/tests/test_database/test_migrations.py b/back/tests/test_database/test_migrations.py index 117e3b57..88b21ab8 100644 --- a/back/tests/test_database/test_migrations.py +++ b/back/tests/test_database/test_migrations.py @@ -2,22 +2,33 @@ from pathlib import Path +import pytest from sqlalchemy.engine import URL from whombat.system import database +from whombat.system.settings import Settings -def test_can_detect_database_that_needs_creation(database_url: URL): - """Test that we can detect a database that needs creation. +@pytest.fixture +def db_path(tmp_path: Path) -> Path: + """Create a temporary database file.""" + return tmp_path / "newdb.db" - We are using the `sqlite+aiosqlite://` url here, which is the - default database url for the application. This url does not create - the database file automatically, so we expect the database to need - creation. - """ - cfg = database.create_alembic_config(database_url, is_async=False) - engine = database.create_sync_db_engine(database_url) +@pytest.fixture +def db_url(db_path: Path) -> URL: + """Create a temporary settings file.""" + settings = Settings(db_name=str(db_path)) + return database.get_database_url(settings) + + +def test_can_detect_database_that_needs_creation(db_path: Path, db_url: URL): + """Test that we can detect a database that needs creation.""" + + assert not db_path.exists() + + cfg = database.create_alembic_config(db_url, is_async=False) + engine = database.create_sync_db_engine(db_url) with engine.connect() as conn: assert ( @@ -26,17 +37,35 @@ def test_can_detect_database_that_needs_creation(database_url: URL): ) +def test_can_detect_database_is_ok(db_path: Path, db_url: URL): + """Test that we can detect a database that needs creation.""" + + assert not db_path.exists() + + cfg = database.create_alembic_config(db_url, is_async=False) + + engine = database.create_sync_db_engine(db_url) + with engine.connect() as conn: + database.create_or_update_db(conn, cfg) + + engine = database.create_sync_db_engine(db_url) + with engine.connect() as conn: + assert database.get_db_state(conn, cfg) == database.DatabaseState.OK + + def test_can_create_database( - database_url: URL, - database_test: Path, + db_url: URL, + db_path: Path, ): """Test that we can create a database.""" - cfg = database.create_alembic_config(database_url, is_async=False) - engine = database.create_sync_db_engine(database_url) + + assert not db_path.exists() + + cfg = database.create_alembic_config(db_url, is_async=False) + engine = database.create_sync_db_engine(db_url) with engine.connect() as conn: - # Create the database database.create_or_update_db(conn, cfg) # Check that the database file exists - assert database_test.exists() + assert db_path.exists() diff --git a/back/tests/test_routers/conftest.py b/back/tests/test_routers/conftest.py index ce91f0cb..d6281b17 100644 --- a/back/tests/test_routers/conftest.py +++ b/back/tests/test_routers/conftest.py @@ -8,7 +8,7 @@ @pytest.fixture -async def client(database_test: Path, settings: Settings): +async def client(database_path: Path, settings: Settings): """Fixture to initialize the test database.""" app = create_app(settings) @@ -17,4 +17,4 @@ async def client(database_test: Path, settings: Settings): with TestClient(app) as client: yield client - database_test.unlink() + database_path.unlink() From 02797b116718eb9466a2d7d8753a4f53fa90b79b Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 14:36:00 +0000 Subject: [PATCH 02/20] WIP adding info to the dev docs --- back/docs/developer_guide/cli.md | 0 .../docs/developer_guide/design_principles.md | 1 - back/docs/developer_guide/index.md | 30 +++++- back/docs/developer_guide/quickstart.md | 94 +++++++++++++++++++ 4 files changed, 121 insertions(+), 4 deletions(-) delete mode 100644 back/docs/developer_guide/cli.md delete mode 100644 back/docs/developer_guide/design_principles.md diff --git a/back/docs/developer_guide/cli.md b/back/docs/developer_guide/cli.md deleted file mode 100644 index e69de29b..00000000 diff --git a/back/docs/developer_guide/design_principles.md b/back/docs/developer_guide/design_principles.md deleted file mode 100644 index 488a540d..00000000 --- a/back/docs/developer_guide/design_principles.md +++ /dev/null @@ -1 +0,0 @@ -# Design Principles diff --git a/back/docs/developer_guide/index.md b/back/docs/developer_guide/index.md index 22d456ec..47390782 100644 --- a/back/docs/developer_guide/index.md +++ b/back/docs/developer_guide/index.md @@ -1,5 +1,29 @@ # Developer Guide -Thank you for your patience as we craft this documentation! We're diligently -working to provide you with valuable insights and instructions. Stay tuned for -updates! +Welcome to the Whombat Developer Guide. This comprehensive resource covers a +variety of topics essential for developers: + +1. [Quickstart](quickstart.md): Learn how to set up a development environment on + your machine quickly. +2. [Architecture](architecture.md): Gain insight into the high-level structure + of Whombat, its main components, and how they come together to form the final + application. +3. [Database Layer](database.md): Explore information about the data stored by + Whombat, its storage configuration, and ways to tailor it to meet specific + requirements. +4. [Python API](api.md): Discover the convenient Python API provided by Whombat + for interacting with stored data. Use this API to integrate Whombat's outputs + into your Python scripts or create custom annotation workflows. +5. [HTTP REST API](rest_api.md): Explore the HTTP REST API that all Whombat + instances provide. This allows third parties to safely interact with Whombat + data, enabling the development of alternative frontends or other web + applications for working with annotation data. +6. [Front End](front_end.md): Delve into the details of how the Whombat frontend + is constructed. +7. [Plugins](plugins.md): Stay tuned for updates on the integration of a plugin + system, offering an easy way to incorporate third-party code into Whombat. + Check here for the latest information. +8. [Contributing](../CONTRIBUTING.md): Find instructions on how to contribute to + this project and become part of the Whombat community. +9. [Code of Conduct](../CODE_OF_CONDUCT.md): Familiarize yourself with our code + of conduct to maintain a friendly and collaborative environment. diff --git a/back/docs/developer_guide/quickstart.md b/back/docs/developer_guide/quickstart.md index acb98436..7f0d8447 100644 --- a/back/docs/developer_guide/quickstart.md +++ b/back/docs/developer_guide/quickstart.md @@ -1 +1,95 @@ # Quickstart + +## Pre-requisites + +Before setting up your Whombat development environment, ensure you have the +following tools installed: + +1. **Python 3.11**: We developed Whombat using this version, but any newer + version should be compatible. Download Python 3.11 + [here](https://www.python.org/downloads/release/python-3117/). + +2. **PDM**: PDM is a Python package dependency manager that we use to manage + dependencies for the Python part of Whombat. Download PDM + [here](https://pdm-project.org/latest/#installation). + +3. **Node.js**: We use Node.js to develop and bundle the final JavaScript code + for the Whombat frontend. Download the latest version + [here](https://nodejs.org/dist/v20.11.0/node-v20.11.0-linux-x64.tar.xz). + +## Set Up a Development Environment + +After confirming that you have all the prerequisites ready, follow these steps +to set up a development environment on your machine. + +1. Clone the repository: + +```bash +git clone https://github.com/mbsantiago/whombat.git +``` + +2. Navigate to the backend directory and install dependencies: + +```bash +cd whombat/back +pdm install --dev +``` + +3. Move to the frontend directory and install all dependencies: + +```bash +cd ../front +npm install +``` + +These instructions ensure you have the necessary tools and dependencies to +kickstart Whombat development on your local machine. + +## Running the Development Server + +Once installed, you can start the backend server by navigating to the `back` +directory and running: + +```bash +pdm run make serve-dev +``` + +You can also start the frontend development server by navigating to the `front` +directory and running: + +```bash +npm run dev +``` + +These commands initiate the development servers for both the backend and +frontend components of Whombat. Navigate to [localhost:3000](localhost:3000) to +access the development front end. + +## Our Standards + +At Whombat, we emphasize code quality and employ various tools to streamline +development. + +### Code Formatting + +We follow the black style for Python to maintain consistent formatting +throughout the project. Additionally, we use isort to organize imports neatly. +For the Typescript project, prettier serves as the primary code formatter. + +### Linting + +We utilize the following tools for linting and error checking: + +1. Python: + - **Ruff** for fast overall error checking + - **Pyright** for type checking + +2. Typescript: + - **Eslint** for linting + - **Tsc** for checking Typescript code + +### Documentation + +We adhere to the Numpy docstring format for documenting Python code. Our +documentation is built using mkdocs, providing a clear and organized structure +for users and contributors. From 7bdb59d307cea1d3adbdf8a61ea6336d152e7677 Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 14:38:23 +0000 Subject: [PATCH 03/20] Updated docs toc --- back/mkdocs.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/back/mkdocs.yml b/back/mkdocs.yml index 0263b3cd..0a993c3e 100644 --- a/back/mkdocs.yml +++ b/back/mkdocs.yml @@ -14,11 +14,9 @@ nav: - developer_guide/index.md - Quickstart: developer_guide/quickstart.md - Architecture: developer_guide/architecture.md - - Design Principles: developer_guide/design_principles.md - Database Layer: developer_guide/database.md - Python API: developer_guide/api.md - HTTP REST API: developer_guide/rest_api.md - - Command Line Interface: developer_guide/cli.md - Front End: developer_guide/front_end.md - Plugins: developer_guide/plugins.md - Contributing: CONTRIBUTING.md From af5fb59ce208a1a74c804e2a297bd10a439d7f77 Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 14:41:17 +0000 Subject: [PATCH 04/20] Added badges --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index f55438e2..e79311b9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Whombat +![build](https://github.com/github/docs/actions/workflows/bundle.yml/badge.svg) +![docs](https://github.com/github/docs/actions/workflows/docs.yml/badge.svg) +![lint](https://github.com/github/docs/actions/workflows/lint.yml/badge.svg) +![tests](https://github.com/github/docs/actions/workflows/test.yml/badge.svg) + **Whombat** is an open-source, web-based audio annotation tool designed to streamline audio data labeling and annotation, with a particular focus on supporting machine learning model development. ## Installation From 8817f8b4b098a5c0a881da431991f036a08b36b6 Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 14:48:46 +0000 Subject: [PATCH 05/20] Badges --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e79311b9..026aaf89 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Whombat -![build](https://github.com/github/docs/actions/workflows/bundle.yml/badge.svg) -![docs](https://github.com/github/docs/actions/workflows/docs.yml/badge.svg) -![lint](https://github.com/github/docs/actions/workflows/lint.yml/badge.svg) -![tests](https://github.com/github/docs/actions/workflows/test.yml/badge.svg) +![build](https://github.com/mbsantiago/whombat/actions/workflows/bundle.yml/badge.svg) +![lint](https://github.com/mbsantiago/whombat/actions/workflows/lint.yml/badge.svg) +![docs](https://github.com/mbsantiago/whombat/actions/workflows/docs.yml/badge.svg) +![tests](https://github.com/mbsantiago/whombat/actions/workflows/test.yml/badge.svg) **Whombat** is an open-source, web-based audio annotation tool designed to streamline audio data labeling and annotation, with a particular focus on supporting machine learning model development. From d42eb5a7ab20642ac72030c74c9deccec9c8a8a7 Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 14:54:04 +0000 Subject: [PATCH 06/20] badges --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 026aaf89..3d49ad74 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Whombat +![GitHub License](https://img.shields.io/github/license/mbsantiago/whombat) +![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=back%2Fpyproject.toml) + +![Static Badge](https://img.shields.io/badge/formatting-black-black) +[![codecov](https://codecov.io/gh/mbsantiago/soundevent/branch/main/graph/badge.svg?token=42kVE87avA)](https://codecov.io/gh/mbsantiago/soundevent) ![build](https://github.com/mbsantiago/whombat/actions/workflows/bundle.yml/badge.svg) ![lint](https://github.com/mbsantiago/whombat/actions/workflows/lint.yml/badge.svg) ![docs](https://github.com/mbsantiago/whombat/actions/workflows/docs.yml/badge.svg) From 61315fa5fd3b2e550adc1545e34eed0fc1e71c26 Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 19:13:34 +0000 Subject: [PATCH 07/20] Making better workflows --- .github/workflows/build_frontend.yml | 27 +++++++++++++++ .github/workflows/build_user_guide.yml | 27 +++++++++++++++ .github/workflows/bundle.yml | 48 ++++++++++++++++++-------- .github/workflows/bundle_test.yml | 48 +++++++++++++++++--------- .github/workflows/test.yml | 9 ++++- Makefile | 3 ++ README.md | 5 ++- back/pyproject.toml | 27 ++++++++++----- front/package-lock.json | 4 +-- scripts/install.sh | 4 +-- scripts/publish_pypi.sh | 14 ++++++++ scripts/update_guide.sh | 4 +-- 12 files changed, 169 insertions(+), 51 deletions(-) create mode 100644 .github/workflows/build_frontend.yml create mode 100644 .github/workflows/build_user_guide.yml create mode 100644 scripts/publish_pypi.sh diff --git a/.github/workflows/build_frontend.yml b/.github/workflows/build_frontend.yml new file mode 100644 index 00000000..e1672b9c --- /dev/null +++ b/.github/workflows/build_frontend.yml @@ -0,0 +1,27 @@ +name: Build Frontend + +on: + workflow_call: + +jobs: + build-frontend: + name: Build Frontend + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v4 + with: + node-version: 21 + - name: Install dependencies + run: | + cd front + npm install + - name: Build app + run: + cd front + npm build + - name: Upload build + uses: actions/upload-artifact@v4 + with: + name: frontend + path: front/out diff --git a/.github/workflows/build_user_guide.yml b/.github/workflows/build_user_guide.yml new file mode 100644 index 00000000..346d847e --- /dev/null +++ b/.github/workflows/build_user_guide.yml @@ -0,0 +1,27 @@ +name: Build Frontend + +on: + workflow_call: + +jobs: + build-user-guide: + name: Build User Guide + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + - name: Install dependencies + run: | + cd back + pip install -r guide_requirements + - name: Build Guide + run: + cd back + mkdocs build -d mkdocs-guide.yml -d site + - name: Upload build + uses: actions/upload-artifact@v4 + with: + name: user_guide + path: back/site diff --git a/.github/workflows/bundle.yml b/.github/workflows/bundle.yml index d830b13a..d101204c 100644 --- a/.github/workflows/bundle.yml +++ b/.github/workflows/bundle.yml @@ -4,20 +4,28 @@ on: types: [published] jobs: + build-user-guide: + - uses: mbsantiago/whombat/.github/workflows/build_user_guide.yml@main + + build-frontend: + - uses: mbsantiago/whombat/.github/workflows/build_frontend.yml@main + build-windows: runs-on: windows-latest + needs: [build-user-guide, build-frontend] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: "3.11" - - uses: actions/setup-node@v4 + - uses: actions/download-artifact@v4 with: - node-version: 21 - - shell: pwsh - run: scripts/update_front.ps1 - - shell: pwsh - run: scripts/update_guide.ps1 + name: frontend + path: back/src/whombat/statics/ + - uses: actions/download-artifact@v4 + with: + name: user_guide + path: back/src/whombat/user_guide/ - shell: pwsh run: scripts/bundle_windows.ps1 - run: | @@ -30,17 +38,20 @@ jobs: build-ubuntu: runs-on: ubuntu-20.04 + needs: [build-user-guide, build-frontend] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: "3.11" - - uses: actions/setup-node@v4 + - uses: actions/download-artifact@v4 with: - node-version: 21 - - run: npm cache clean --force - - run: make build-frontend - - run: make build-guide + name: frontend + path: back/src/whombat/statics/ + - uses: actions/download-artifact@v4 + with: + name: user_guide + path: back/src/whombat/user_guide/ - run: bash scripts/bundle_linux.sh - run: | mkdir dist/ @@ -52,16 +63,23 @@ jobs: build-macos: runs-on: macos-11 + needs: [build-user-guide, build-frontend] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: "3.11" - - uses: actions/setup-node@v4 + - uses: actions/setup-python@v4 + with: + python-version: "3.11" + - uses: actions/download-artifact@v4 + with: + name: frontend + path: back/src/whombat/statics/ + - uses: actions/download-artifact@v4 with: - node-version: 21 - - run: make build-frontend - - run: make build-guide + name: user_guide + path: back/src/whombat/user_guide/ - run: bash scripts/bundle_macos.sh - run: | mkdir dist/ diff --git a/.github/workflows/bundle_test.yml b/.github/workflows/bundle_test.yml index 8b658147..019b3b5c 100644 --- a/.github/workflows/bundle_test.yml +++ b/.github/workflows/bundle_test.yml @@ -1,27 +1,34 @@ name: Test Bundle App -on: +on: workflow_dispatch: push: branches: - workflows - jobs: + build-user-guide: + - uses: mbsantiago/whombat/.github/workflows/build_user_guide.yml@main + + build-frontend: + - uses: mbsantiago/whombat/.github/workflows/build_frontend.yml@main + build-windows-test: runs-on: windows-latest + needs: [build-user-guide, build-frontend] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: "3.11" - - uses: actions/setup-node@v4 + - uses: actions/download-artifact@v4 with: - node-version: 21 - - shell: pwsh - run: scripts/update_front.ps1 - - shell: pwsh - run: scripts/update_guide.ps1 + name: frontend + path: back/src/whombat/statics/ + - uses: actions/download-artifact@v4 + with: + name: user_guide + path: back/src/whombat/user_guide/ - shell: pwsh run: scripts/bundle_windows.ps1 - run: | @@ -34,17 +41,20 @@ jobs: build-ubuntu-test: runs-on: ubuntu-20.04 + needs: [build-user-guide, build-frontend] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: "3.11" - - uses: actions/setup-node@v4 + - uses: actions/download-artifact@v4 with: - node-version: 21 - - run: npm cache clean --force - - run: make build-frontend - - run: make build-guide + name: frontend + path: back/src/whombat/statics/ + - uses: actions/download-artifact@v4 + with: + name: user_guide + path: back/src/whombat/user_guide/ - run: bash scripts/bundle_linux.sh - run: | mkdir dist/ @@ -56,16 +66,20 @@ jobs: build-macos-test: runs-on: macos-11 + needs: [build-user-guide, build-frontend] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: "3.11" - - uses: actions/setup-node@v4 + - uses: actions/download-artifact@v4 + with: + name: frontend + path: back/src/whombat/statics/ + - uses: actions/download-artifact@v4 with: - node-version: 21 - - run: make build-frontend - - run: make build-guide + name: user_guide + path: back/src/whombat/user_guide/ - run: bash scripts/bundle_macos.sh - run: | mkdir dist/ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 92cedbaa..1391c511 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,4 +24,11 @@ jobs: - name: Run tests run: | cd back - pdm run pytest tests/ + pdm run coverage run --source whombat -m pytest + pdm run coverage report -m + pdm run coverage html + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + diff --git a/Makefile b/Makefile index f7ee914d..9cda3293 100644 --- a/Makefile +++ b/Makefile @@ -11,3 +11,6 @@ build-guide: bundle-pyinstaller: bash scripts/bundle_linux.sh + +publish: + bash scripts/publish_pypi.sh diff --git a/README.md b/README.md index 3d49ad74..1b72a3c6 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ # Whombat ![GitHub License](https://img.shields.io/github/license/mbsantiago/whombat) -![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=back%2Fpyproject.toml) - +![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fmbsantiago%2Fwhombat%2Fdev%2Fback%2Fpyproject.toml) ![Static Badge](https://img.shields.io/badge/formatting-black-black) -[![codecov](https://codecov.io/gh/mbsantiago/soundevent/branch/main/graph/badge.svg?token=42kVE87avA)](https://codecov.io/gh/mbsantiago/soundevent) +[![codecov](https://codecov.io/gh/mbsantiago/whombat/graph/badge.svg?token=WMzUfSXIyL)](https://codecov.io/gh/mbsantiago/whombat) ![build](https://github.com/mbsantiago/whombat/actions/workflows/bundle.yml/badge.svg) ![lint](https://github.com/mbsantiago/whombat/actions/workflows/lint.yml/badge.svg) ![docs](https://github.com/mbsantiago/whombat/actions/workflows/docs.yml/badge.svg) diff --git a/back/pyproject.toml b/back/pyproject.toml index b738e33b..d11d8695 100644 --- a/back/pyproject.toml +++ b/back/pyproject.toml @@ -2,9 +2,7 @@ name = "Whombat" version = "0.4.0" description = "Audio Annotation Tool" -authors = [ - {name = "Santiago Martinez", email = "santiago.mbal@gmail.com"}, -] +authors = [{ name = "Santiago Martinez", email = "santiago.mbal@gmail.com" }] dependencies = [ "uvicorn[standard]>=0.20.0", "aiosqlite>=0.18.0", @@ -22,7 +20,14 @@ dependencies = [ ] requires-python = ">=3.11,<3.12" readme = "README.md" -license = {text = "MIT"} +keywords = ["audio", "annotation", "tool", "bioacoustics", "machine learning"] +license = { file = "LICENSE" } + +[project.urls] +Homepage = "https://github.com/mbsantiago/whombat" +Documentation = "https://mbsantiago.github.io/whombat/" +"Isue Tracker" = "https://github.com/mbsantiago/whombat/issues" +Releases = "https://github.com/mbsantiago/whombat/releases" [project.optional-dependencies] docs = [ @@ -30,14 +35,16 @@ docs = [ "mkdocs-material>=9.4.14", "mkdocstrings[python]>=0.24.0", ] -postgre = [ - "asyncpg>=0.29.0", - "psycopg2-binary>=2.9.9", -] +postgre = ["asyncpg>=0.29.0", "psycopg2-binary>=2.9.9"] + [build-system] requires = ["hatchling"] build-backend = "hatchling.build" +[tool.hatch.build.targets.wheel.force-include] +"src/whombat/migrations/versions/" = "whombat/migrations/versions/" +"src/whombat/statics/" = "whombat/statics/" + [tool.black] line-length = 79 @@ -74,5 +81,7 @@ dev = [ "ruff>=0.1.7", "icecream>=2.1.3", "pytest-xdist>=3.5.0", + "mkdocs>=1.5.3", + "mkdocs-material>=9.4.14", + "mkdocstrings[python]>=0.24.0", ] - diff --git a/front/package-lock.json b/front/package-lock.json index 9189db63..d27b6b76 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -1,12 +1,12 @@ { "name": "whombat-front", - "version": "0.3.0", + "version": "0.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "whombat-front", - "version": "0.3.0", + "version": "0.4.0", "dependencies": { "@headlessui-float/react": "^0.13.0", "@headlessui/react": "^1.7.17", diff --git a/scripts/install.sh b/scripts/install.sh index acaddd40..03b18642 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -3,8 +3,8 @@ # Make sure you have python 3.11 installed if [[ ! $(python --version) =~ "3.11" ]]; then - echo "Please install python 3.11" - exit 1 + echo "Please install python 3.11" + exit 1 fi # Move to the root directory of the backend diff --git a/scripts/publish_pypi.sh b/scripts/publish_pypi.sh new file mode 100644 index 00000000..50c1f3e7 --- /dev/null +++ b/scripts/publish_pypi.sh @@ -0,0 +1,14 @@ + +# Update front-end +bash scripts/update_front.sh + +# Update user-guide +bash scripts/update_guide.sh + +# Install the dependencies and whombat as an editable package +cd back/ + +# Activate virtual environment +source .venv/bin/activate + +pip install build diff --git a/scripts/update_guide.sh b/scripts/update_guide.sh index 904a2d39..1aac85c6 100644 --- a/scripts/update_guide.sh +++ b/scripts/update_guide.sh @@ -6,13 +6,13 @@ cd back # Make sure there is a virtual environment if [ ! -d .venv ]; then - python -m venv .venv + python -m venv .venv fi # Activate virtual environment source .venv/bin/activate -pip install .[docs] +pip install -r guide_requirements.txt # Build the user guide make build-guide From 44d56a54954a02e3823dc6c27f7ed42d843a838f Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 19:15:23 +0000 Subject: [PATCH 08/20] workflows --- .github/workflows/bundle.yml | 4 ++-- .github/workflows/bundle_test.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/bundle.yml b/.github/workflows/bundle.yml index d101204c..a757ee48 100644 --- a/.github/workflows/bundle.yml +++ b/.github/workflows/bundle.yml @@ -5,10 +5,10 @@ on: jobs: build-user-guide: - - uses: mbsantiago/whombat/.github/workflows/build_user_guide.yml@main + uses: mbsantiago/whombat/.github/workflows/build_user_guide.yml@main build-frontend: - - uses: mbsantiago/whombat/.github/workflows/build_frontend.yml@main + uses: mbsantiago/whombat/.github/workflows/build_frontend.yml@main build-windows: runs-on: windows-latest diff --git a/.github/workflows/bundle_test.yml b/.github/workflows/bundle_test.yml index 019b3b5c..e1015a7a 100644 --- a/.github/workflows/bundle_test.yml +++ b/.github/workflows/bundle_test.yml @@ -8,10 +8,10 @@ on: jobs: build-user-guide: - - uses: mbsantiago/whombat/.github/workflows/build_user_guide.yml@main + uses: mbsantiago/whombat/.github/workflows/build_user_guide.yml@main build-frontend: - - uses: mbsantiago/whombat/.github/workflows/build_frontend.yml@main + uses: mbsantiago/whombat/.github/workflows/build_frontend.yml@main build-windows-test: runs-on: windows-latest From 12c8d9a61b5be0d6fe4b21dcd9861d04eb321f93 Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 19:16:34 +0000 Subject: [PATCH 09/20] dev workflows --- .github/workflows/bundle.yml | 4 ++-- .github/workflows/bundle_test.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/bundle.yml b/.github/workflows/bundle.yml index a757ee48..d6df6d13 100644 --- a/.github/workflows/bundle.yml +++ b/.github/workflows/bundle.yml @@ -5,10 +5,10 @@ on: jobs: build-user-guide: - uses: mbsantiago/whombat/.github/workflows/build_user_guide.yml@main + uses: mbsantiago/whombat/.github/workflows/build_user_guide.yml@dev build-frontend: - uses: mbsantiago/whombat/.github/workflows/build_frontend.yml@main + uses: mbsantiago/whombat/.github/workflows/build_frontend.yml@dev build-windows: runs-on: windows-latest diff --git a/.github/workflows/bundle_test.yml b/.github/workflows/bundle_test.yml index e1015a7a..ce848459 100644 --- a/.github/workflows/bundle_test.yml +++ b/.github/workflows/bundle_test.yml @@ -8,10 +8,10 @@ on: jobs: build-user-guide: - uses: mbsantiago/whombat/.github/workflows/build_user_guide.yml@main + uses: mbsantiago/whombat/.github/workflows/build_user_guide.yml@dev build-frontend: - uses: mbsantiago/whombat/.github/workflows/build_frontend.yml@main + uses: mbsantiago/whombat/.github/workflows/build_frontend.yml@dev build-windows-test: runs-on: windows-latest From d35e7c791066c1aee20e57dc2f235da4d70c1aeb Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 19:17:45 +0000 Subject: [PATCH 10/20] fix typo in requirements.txt --- .github/workflows/build_user_guide.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_user_guide.yml b/.github/workflows/build_user_guide.yml index 346d847e..28e9b8df 100644 --- a/.github/workflows/build_user_guide.yml +++ b/.github/workflows/build_user_guide.yml @@ -15,7 +15,7 @@ jobs: - name: Install dependencies run: | cd back - pip install -r guide_requirements + pip install -r guide_requirements.txt - name: Build Guide run: cd back From e8234c617f45808aa05126b691375deed130fa2e Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 19:18:15 +0000 Subject: [PATCH 11/20] fix typo in workflow --- .github/workflows/build_frontend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_frontend.yml b/.github/workflows/build_frontend.yml index e1672b9c..fe9323d5 100644 --- a/.github/workflows/build_frontend.yml +++ b/.github/workflows/build_frontend.yml @@ -17,7 +17,7 @@ jobs: cd front npm install - name: Build app - run: + run: | cd front npm build - name: Upload build From f59ba53242657ff1ccaae89cae1d996071c5391b Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 19:20:16 +0000 Subject: [PATCH 12/20] typos in workflows --- .github/workflows/build_frontend.yml | 2 +- .github/workflows/build_user_guide.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_frontend.yml b/.github/workflows/build_frontend.yml index fe9323d5..fe65c42d 100644 --- a/.github/workflows/build_frontend.yml +++ b/.github/workflows/build_frontend.yml @@ -19,7 +19,7 @@ jobs: - name: Build app run: | cd front - npm build + npm run build - name: Upload build uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/build_user_guide.yml b/.github/workflows/build_user_guide.yml index 28e9b8df..f7c27ccd 100644 --- a/.github/workflows/build_user_guide.yml +++ b/.github/workflows/build_user_guide.yml @@ -17,7 +17,7 @@ jobs: cd back pip install -r guide_requirements.txt - name: Build Guide - run: + run: | cd back mkdocs build -d mkdocs-guide.yml -d site - name: Upload build From 67c63ae28de1bbdeac384fb809b51d677a547db9 Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 20:05:08 +0000 Subject: [PATCH 13/20] Adding publish workflow --- .github/workflows/build_frontend.yml | 2 +- .github/workflows/build_user_guide.yml | 4 +- .github/workflows/bundle_test.yml | 12 +-- .github/workflows/docs.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/publish.yml | 108 +++++++++++++++++++++++++ .github/workflows/test.yml | 2 +- scripts/bundle_linux.sh | 1 + scripts/bundle_macos.sh | 1 + scripts/bundle_windows.ps1 | 1 + 10 files changed, 123 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/build_frontend.yml b/.github/workflows/build_frontend.yml index fe65c42d..6299d42d 100644 --- a/.github/workflows/build_frontend.yml +++ b/.github/workflows/build_frontend.yml @@ -8,7 +8,7 @@ jobs: name: Build Frontend runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 21 diff --git a/.github/workflows/build_user_guide.yml b/.github/workflows/build_user_guide.yml index f7c27ccd..0fe868a4 100644 --- a/.github/workflows/build_user_guide.yml +++ b/.github/workflows/build_user_guide.yml @@ -8,10 +8,10 @@ jobs: name: Build User Guide runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: "3.10" - name: Install dependencies run: | cd back diff --git a/.github/workflows/bundle_test.yml b/.github/workflows/bundle_test.yml index ce848459..805db259 100644 --- a/.github/workflows/bundle_test.yml +++ b/.github/workflows/bundle_test.yml @@ -17,8 +17,8 @@ jobs: runs-on: windows-latest needs: [build-user-guide, build-frontend] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: "3.11" - uses: actions/download-artifact@v4 @@ -43,8 +43,8 @@ jobs: runs-on: ubuntu-20.04 needs: [build-user-guide, build-frontend] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: "3.11" - uses: actions/download-artifact@v4 @@ -68,8 +68,8 @@ jobs: runs-on: macos-11 needs: [build-user-guide, build-frontend] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: "3.11" - uses: actions/download-artifact@v4 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9d2815d1..5cd80970 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -13,7 +13,7 @@ jobs: matrix: python-version: ["3.11"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup PDM uses: pdm-project/setup-pdm@v3 with: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a3872a38..d3fb48ff 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: matrix: python-version: ["3.11"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup PDM uses: pdm-project/setup-pdm@v3 with: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..2670ae63 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,108 @@ +name: Publish PyPI +on: + workflow_dispatch: + release: + types: [published] + +jobs: + build-user-guide: + uses: mbsantiago/whombat/.github/workflows/build_user_guide.yml@dev + + build-frontend: + uses: mbsantiago/whombat/.github/workflows/build_frontend.yml@dev + + build: + runs-on: ubuntu-latest + needs: [build-user-guide, build-frontend] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.11" + - uses: actions/download-artifact@v4 + with: + name: frontend + path: back/src/whombat/statics/ + - uses: actions/download-artifact@v4 + with: + name: user_guide + path: back/src/whombat/user_guide/ + - name: Install pypa/build + run: pip install build + - name: Build a binary wheel and source tarball + run: | + cd back + python -m build + - name: Store the distribution packages + uses: actions/upload-artifact@v3 + with: + name: python-package-distributions + path: back/dist/ + + publish-to-pypi: + name: Publish to PyPI + needs: [build] + runs-on: ubuntu-latest + + if: github.event_name == 'release' + + environment: + name: pypi + url: https://pypi.org/p/whombat + + permissions: + id-token: write + steps: + - name: Download all the dists + uses: actions/download-artifact@v3 + with: + name: python-package-distributions + path: dist/ + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + + github-release: + name: Upload to GitHub release + needs: [publish-to-pypi] + runs-on: ubuntu-latest + + if: github.event_name == 'release' + + permissions: + contents: write + id-token: write + + steps: + - name: Download the dists + uses: actions/download-artifact@v3 + with: + name: python-package-distributions + path: dist/ + - name: Upload artifact to GitHub Release + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + gh release upload '${{ github.ref_name }}' dist/** --repo '${{github.repository }}' + + publish-to-testpypi: + name: Publish to TestPyPI + needs: [build] + runs-on: ubuntu-latest + + if: github.event_name == 'workflow_dispatch' + + environment: + name: testpypi + url: https://test.pypi.org/p/whombat + + permissions: + id-token: write + + steps: + - name: Download all the dists + uses: actions/download-artifact@v3 + with: + name: python-package-distributions + path: dist/ + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1391c511..174b7eba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: matrix: python-version: ["3.11"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup PDM uses: pdm-project/setup-pdm@v3 with: diff --git a/scripts/bundle_linux.sh b/scripts/bundle_linux.sh index 1ca2a15d..8957d3da 100644 --- a/scripts/bundle_linux.sh +++ b/scripts/bundle_linux.sh @@ -38,6 +38,7 @@ build/.venv/bin/pyinstaller \ --add-data "src/whombat/statics:whombat/statics" \ --add-data "src/whombat/user_guide:whombat/user_guide" \ --add-data "alembic.ini:." \ + --recursive-copy-metadata "numpy" \ --name whombat \ --onefile \ --splash "../assets/splash.png" \ diff --git a/scripts/bundle_macos.sh b/scripts/bundle_macos.sh index 6f771f50..d4df16c6 100644 --- a/scripts/bundle_macos.sh +++ b/scripts/bundle_macos.sh @@ -38,6 +38,7 @@ build/.venv/bin/pyinstaller \ --add-data "src/whombat/statics:whombat/statics" \ --add-data "src/whombat/user_guide:whombat/user_guide" \ --add-data "alembic.ini:." \ + --recursive-copy-metadata "numpy" \ --name whombat \ --onefile \ --console \ diff --git a/scripts/bundle_windows.ps1 b/scripts/bundle_windows.ps1 index a26599b8..a8333fa8 100644 --- a/scripts/bundle_windows.ps1 +++ b/scripts/bundle_windows.ps1 @@ -35,6 +35,7 @@ build\.venv\Scripts\pyinstaller ` --add-data "src\whombat\statics;whombat\statics" ` --add-data "src\whombat\user_guide;whombat\user_guide" ` --add-data "alembic.ini;." ` + --recursive-copy-metadata "numpy" ` --name whombat ` --onefile ` --console ` From f3bfe7c9ca0f902893adcc9d68a57635334cb48f Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 20:33:46 +0000 Subject: [PATCH 14/20] fix publish workflow --- .github/workflows/publish.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2670ae63..d85b4f73 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,8 +15,8 @@ jobs: runs-on: ubuntu-latest needs: [build-user-guide, build-frontend] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: "3.11" - uses: actions/download-artifact@v4 @@ -34,7 +34,7 @@ jobs: cd back python -m build - name: Store the distribution packages - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: python-package-distributions path: back/dist/ @@ -54,7 +54,7 @@ jobs: id-token: write steps: - name: Download all the dists - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: python-package-distributions path: dist/ @@ -74,7 +74,7 @@ jobs: steps: - name: Download the dists - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: python-package-distributions path: dist/ @@ -100,9 +100,11 @@ jobs: steps: - name: Download all the dists - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: python-package-distributions path: dist/ - name: Publish distribution 📦 to PyPI uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ From 46baddff3600c12359b4df98f012fe0f9ea0c348 Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 21:33:40 +0000 Subject: [PATCH 15/20] Updated dependency lockfile --- back/pdm.lock | 548 ++++++++++++++++++++++++-------------------- back/pyproject.toml | 1 + 2 files changed, 295 insertions(+), 254 deletions(-) diff --git a/back/pdm.lock b/back/pdm.lock index 10194234..119b6e96 100644 --- a/back/pdm.lock +++ b/back/pdm.lock @@ -4,8 +4,8 @@ [metadata] groups = ["default", "dev", "docs", "postgre"] strategy = ["cross_platform"] -lock_version = "4.4" -content_hash = "sha256:cb8a3892be1ed4be6d348e02486b2dd7d541d966b96b06620c917e16d94d0458" +lock_version = "4.4.1" +content_hash = "sha256:5370cd26b867ebd0a6c6829cc1678a277f998902dcc6f1acc5d0687293094535" [[package]] name = "aiosqlite" @@ -152,7 +152,7 @@ files = [ [[package]] name = "black" -version = "23.12.1" +version = "24.1.1" requires_python = ">=3.8" summary = "The uncompromising code formatter." dependencies = [ @@ -163,12 +163,12 @@ dependencies = [ "platformdirs>=2", ] files = [ - {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, - {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, - {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, - {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, - {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, - {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, + {file = "black-24.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc1ec9aa6f4d98d022101e015261c056ddebe3da6a8ccfc2c792cbe0349d48b7"}, + {file = "black-24.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0269dfdea12442022e88043d2910429bed717b2d04523867a85dacce535916b8"}, + {file = "black-24.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3d64db762eae4a5ce04b6e3dd745dcca0fb9560eb931a5be97472e38652a161"}, + {file = "black-24.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5d7b06ea8816cbd4becfe5f70accae953c53c0e53aa98730ceccb0395520ee5d"}, + {file = "black-24.1.1-py3-none-any.whl", hash = "sha256:5cdc2e2195212208fbcae579b931407c1fa9997584f0a415421748aeafff1168"}, + {file = "black-24.1.1.tar.gz", hash = "sha256:48b5760dcbfe5cf97fd4fba23946681f3a81514c6ab8a45b50da67ac8fbc6c7b"}, ] [[package]] @@ -299,56 +299,89 @@ files = [ [[package]] name = "coverage" -version = "7.4.0" +version = "7.4.1" requires_python = ">=3.8" summary = "Code coverage measurement for Python" files = [ - {file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"}, - {file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"}, - {file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"}, - {file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"}, - {file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"}, - {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6"}, + {file = "coverage-7.4.1-cp311-cp311-win32.whl", hash = "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5"}, + {file = "coverage-7.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968"}, + {file = "coverage-7.4.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166"}, + {file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"}, +] + +[[package]] +name = "coverage" +version = "7.4.1" +extras = ["toml"] +requires_python = ">=3.8" +summary = "Code coverage measurement for Python" +dependencies = [ + "coverage==7.4.1", +] +files = [ + {file = "coverage-7.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6"}, + {file = "coverage-7.4.1-cp311-cp311-win32.whl", hash = "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5"}, + {file = "coverage-7.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968"}, + {file = "coverage-7.4.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166"}, + {file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"}, ] [[package]] name = "cryptography" -version = "41.0.7" +version = "42.0.2" requires_python = ">=3.7" summary = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." dependencies = [ - "cffi>=1.12", -] -files = [ - {file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf"}, - {file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1"}, - {file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157"}, - {file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406"}, - {file = "cryptography-41.0.7-cp37-abi3-win32.whl", hash = "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d"}, - {file = "cryptography-41.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309"}, - {file = "cryptography-41.0.7.tar.gz", hash = "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc"}, + "cffi>=1.12; platform_python_implementation != \"PyPy\"", +] +files = [ + {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be"}, + {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2"}, + {file = "cryptography-42.0.2-cp37-abi3-win32.whl", hash = "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee"}, + {file = "cryptography-42.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee"}, + {file = "cryptography-42.0.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33"}, + {file = "cryptography-42.0.2-cp39-abi3-win32.whl", hash = "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635"}, + {file = "cryptography-42.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65"}, + {file = "cryptography-42.0.2.tar.gz", hash = "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888"}, ] [[package]] @@ -363,19 +396,20 @@ files = [ [[package]] name = "cython" -version = "3.0.7" +version = "3.0.8" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" summary = "The Cython compiler for writing C extensions in the Python language." files = [ - {file = "Cython-3.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c636c9ab92c7838231a1ba769e519d953af8294612f3f772a54d3a5250ff23f"}, - {file = "Cython-3.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22d2a684122dfb531853d57c8c85c1d5d44be709e12466dca99fa6aee7d8054f"}, - {file = "Cython-3.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1bdf8a107fdf9e174991aa87a0be7504f60de1ec6bfb1ccfb30e33acac818a0"}, - {file = "Cython-3.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3a83e04fde663b84905f3a20213a4333d13a07b79434300704b70dc552761f8b"}, - {file = "Cython-3.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e34b4b08d795ccca920fa26b099558f4f1e4e3f794e4ba8d3433c5bc2454d50a"}, - {file = "Cython-3.0.7-cp311-cp311-win32.whl", hash = "sha256:133057ac45b6fa7fe5d7baada9d3545d09339432f75c0545f556e8c6fecc2932"}, - {file = "Cython-3.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:b65abca78aa5ebc8675c8480b9a53006f6efea9910ad099cf32c9fb5617ef251"}, - {file = "Cython-3.0.7-py2.py3-none-any.whl", hash = "sha256:936ec37b261b226d7404eff23a9aad284098338150d42a53d6a9af12b18d3892"}, - {file = "Cython-3.0.7.tar.gz", hash = "sha256:fb299acf3a578573c190c858d49e0cf9d75f4bc49c3f24c5a63804997ef09213"}, + {file = "Cython-3.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aae26f9663e50caf9657148403d9874eea41770ecdd6caf381d177c2b1bb82ba"}, + {file = "Cython-3.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:547eb3cdb2f8c6f48e6865d5a741d9dd051c25b3ce076fbca571727977b28ac3"}, + {file = "Cython-3.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a567d4b9ba70b26db89d75b243529de9e649a2f56384287533cf91512705bee"}, + {file = "Cython-3.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51d1426263b0e82fb22bda8ea60dc77a428581cc19e97741011b938445d383f1"}, + {file = "Cython-3.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c26daaeccda072459b48d211415fd1e5507c06bcd976fa0d5b8b9f1063467d7b"}, + {file = "Cython-3.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:289ce7838208211cd166e975865fd73b0649bf118170b6cebaedfbdaf4a37795"}, + {file = "Cython-3.0.8-cp311-cp311-win32.whl", hash = "sha256:c8aa05f5e17f8042a3be052c24f2edc013fb8af874b0bf76907d16c51b4e7871"}, + {file = "Cython-3.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:000dc9e135d0eec6ecb2b40a5b02d0868a2f8d2e027a41b0fe16a908a9e6de02"}, + {file = "Cython-3.0.8-py2.py3-none-any.whl", hash = "sha256:171b27051253d3f9108e9759e504ba59ff06e7f7ba944457f94deaf9c21bf0b6"}, + {file = "Cython-3.0.8.tar.gz", hash = "sha256:8333423d8fd5765e7cceea3a9985dd1e0a5dfeb2734629e1a2ed2d6233d39de6"}, ] [[package]] @@ -389,12 +423,12 @@ files = [ [[package]] name = "dnspython" -version = "2.4.2" -requires_python = ">=3.8,<4.0" +version = "2.5.0" +requires_python = ">=3.8" summary = "DNS toolkit" files = [ - {file = "dnspython-2.4.2-py3-none-any.whl", hash = "sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8"}, - {file = "dnspython-2.4.2.tar.gz", hash = "sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984"}, + {file = "dnspython-2.5.0-py3-none-any.whl", hash = "sha256:6facdf76b73c742ccf2d07add296f178e629da60be23ce4b0a9c927b1e02c3a6"}, + {file = "dnspython-2.5.0.tar.gz", hash = "sha256:a0034815a59ba9ae888946be7ccca8f7c157b286f8455b379c692efb51022a15"}, ] [[package]] @@ -433,17 +467,17 @@ files = [ [[package]] name = "fastapi" -version = "0.108.0" +version = "0.109.0" requires_python = ">=3.8" summary = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" dependencies = [ "pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4", - "starlette<0.33.0,>=0.29.0", + "starlette<0.36.0,>=0.35.0", "typing-extensions>=4.8.0", ] files = [ - {file = "fastapi-0.108.0-py3-none-any.whl", hash = "sha256:8c7bc6d315da963ee4cdb605557827071a9a7f95aeb8fcdd3bde48cdc8764dd7"}, - {file = "fastapi-0.108.0.tar.gz", hash = "sha256:5056e504ac6395bf68493d71fcfc5352fdbd5fda6f88c21f6420d80d81163296"}, + {file = "fastapi-0.109.0-py3-none-any.whl", hash = "sha256:8c77515984cd8e8cfeb58364f8cc7a28f0692088475e2614f7bf03275eba9093"}, + {file = "fastapi-0.109.0.tar.gz", hash = "sha256:b978095b9ee01a5cf49b19f4bc1ac9b8ca83aa076e770ef8fd9af09a2b88d191"}, ] [[package]] @@ -535,20 +569,20 @@ files = [ [[package]] name = "fonttools" -version = "4.47.0" +version = "4.47.2" requires_python = ">=3.8" summary = "Tools to manipulate font files" files = [ - {file = "fonttools-4.47.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:854421e328d47d70aa5abceacbe8eef231961b162c71cbe7ff3f47e235e2e5c5"}, - {file = "fonttools-4.47.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:511482df31cfea9f697930f61520f6541185fa5eeba2fa760fe72e8eee5af88b"}, - {file = "fonttools-4.47.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce0e2c88c8c985b7b9a7efcd06511fb0a1fe3ddd9a6cd2895ef1dbf9059719d7"}, - {file = "fonttools-4.47.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7a0a8848726956e9d9fb18c977a279013daadf0cbb6725d2015a6dd57527992"}, - {file = "fonttools-4.47.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e869da810ae35afb3019baa0d0306cdbab4760a54909c89ad8904fa629991812"}, - {file = "fonttools-4.47.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dd23848f877c3754f53a4903fb7a593ed100924f9b4bff7d5a4e2e8a7001ae11"}, - {file = "fonttools-4.47.0-cp311-cp311-win32.whl", hash = "sha256:bf1810635c00f7c45d93085611c995fc130009cec5abdc35b327156aa191f982"}, - {file = "fonttools-4.47.0-cp311-cp311-win_amd64.whl", hash = "sha256:61df4dee5d38ab65b26da8efd62d859a1eef7a34dcbc331299a28e24d04c59a7"}, - {file = "fonttools-4.47.0-py3-none-any.whl", hash = "sha256:d6477ba902dd2d7adda7f0fd3bfaeb92885d45993c9e1928c9f28fc3961415f7"}, - {file = "fonttools-4.47.0.tar.gz", hash = "sha256:ec13a10715eef0e031858c1c23bfaee6cba02b97558e4a7bfa089dba4a8c2ebf"}, + {file = "fonttools-4.47.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:29495d6d109cdbabe73cfb6f419ce67080c3ef9ea1e08d5750240fd4b0c4763b"}, + {file = "fonttools-4.47.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0a1d313a415eaaba2b35d6cd33536560deeebd2ed758b9bfb89ab5d97dc5deac"}, + {file = "fonttools-4.47.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90f898cdd67f52f18049250a6474185ef6544c91f27a7bee70d87d77a8daf89c"}, + {file = "fonttools-4.47.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3480eeb52770ff75140fe7d9a2ec33fb67b07efea0ab5129c7e0c6a639c40c70"}, + {file = "fonttools-4.47.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0255dbc128fee75fb9be364806b940ed450dd6838672a150d501ee86523ac61e"}, + {file = "fonttools-4.47.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f791446ff297fd5f1e2247c188de53c1bfb9dd7f0549eba55b73a3c2087a2703"}, + {file = "fonttools-4.47.2-cp311-cp311-win32.whl", hash = "sha256:740947906590a878a4bde7dd748e85fefa4d470a268b964748403b3ab2aeed6c"}, + {file = "fonttools-4.47.2-cp311-cp311-win_amd64.whl", hash = "sha256:63fbed184979f09a65aa9c88b395ca539c94287ba3a364517698462e13e457c9"}, + {file = "fonttools-4.47.2-py3-none-any.whl", hash = "sha256:7eb7ad665258fba68fd22228a09f347469d95a97fb88198e133595947a20a184"}, + {file = "fonttools-4.47.2.tar.gz", hash = "sha256:7df26dd3650e98ca45f1e29883c96a0b9f5bb6af8d632a6a108bc744fa0bd9b3"}, ] [[package]] @@ -583,15 +617,15 @@ files = [ [[package]] name = "griffe" -version = "0.38.1" +version = "0.40.0" requires_python = ">=3.8" summary = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." dependencies = [ "colorama>=0.4", ] files = [ - {file = "griffe-0.38.1-py3-none-any.whl", hash = "sha256:334c79d3b5964ade65c05dfcaf53518c576dedd387aaba5c9fd71212f34f1483"}, - {file = "griffe-0.38.1.tar.gz", hash = "sha256:bd68d7da7f3d87bc57eb9962b250db123efd9bbcc06c11c1a91b6e583b2a9361"}, + {file = "griffe-0.40.0-py3-none-any.whl", hash = "sha256:db1da6d1d8e08cbb20f1a7dee8c09da940540c2d4c1bfa26a9091cf6fc36a9ec"}, + {file = "griffe-0.40.0.tar.gz", hash = "sha256:76c4439eaa2737af46ae003c331ab6ca79c5365b552f7b5aed263a3b4125735b"}, ] [[package]] @@ -688,15 +722,15 @@ files = [ [[package]] name = "jinja2" -version = "3.1.2" +version = "3.1.3" requires_python = ">=3.7" summary = "A very fast and expressive template engine." dependencies = [ "MarkupSafe>=2.0", ] files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [[package]] @@ -759,44 +793,44 @@ files = [ [[package]] name = "mako" -version = "1.3.0" +version = "1.3.2" requires_python = ">=3.8" summary = "A super-fast templating language that borrows the best ideas from the existing templating languages." dependencies = [ "MarkupSafe>=0.9.2", ] files = [ - {file = "Mako-1.3.0-py3-none-any.whl", hash = "sha256:57d4e997349f1a92035aa25c17ace371a4213f2ca42f99bee9a602500cfd54d9"}, - {file = "Mako-1.3.0.tar.gz", hash = "sha256:e3a9d388fd00e87043edbe8792f45880ac0114e9c4adc69f6e9bfb2c55e3b11b"}, + {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, + {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, ] [[package]] name = "markdown" -version = "3.5.1" +version = "3.5.2" requires_python = ">=3.8" summary = "Python implementation of John Gruber's Markdown." files = [ - {file = "Markdown-3.5.1-py3-none-any.whl", hash = "sha256:5874b47d4ee3f0b14d764324d2c94c03ea66bee56f2d929da9f2508d65e722dc"}, - {file = "Markdown-3.5.1.tar.gz", hash = "sha256:b65d7beb248dc22f2e8a31fb706d93798093c308dc1aba295aedeb9d41a813bd"}, + {file = "Markdown-3.5.2-py3-none-any.whl", hash = "sha256:d43323865d89fc0cb9b20c75fc8ad313af307cc087e84b657d9eec768eddeadd"}, + {file = "Markdown-3.5.2.tar.gz", hash = "sha256:e1ac7b3dc550ee80e602e71c1d168002f062e49f1b11e26a36264dafd4df2ef8"}, ] [[package]] name = "markupsafe" -version = "2.1.3" +version = "2.1.4" requires_python = ">=3.7" summary = "Safely add untrusted strings to HTML/XML markup." files = [ - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"}, + {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"}, ] [[package]] @@ -889,7 +923,7 @@ files = [ [[package]] name = "mkdocs-material" -version = "9.5.3" +version = "9.5.6" requires_python = ">=3.8" summary = "Documentation that simply works" dependencies = [ @@ -906,8 +940,8 @@ dependencies = [ "requests~=2.26", ] files = [ - {file = "mkdocs_material-9.5.3-py3-none-any.whl", hash = "sha256:76c93a8525cceb0b395b9cedab3428bf518cf6439adef2b940f1c1574b775d89"}, - {file = "mkdocs_material-9.5.3.tar.gz", hash = "sha256:5899219f422f0a6de784232d9d40374416302ffae3c160cacc72969fcc1ee372"}, + {file = "mkdocs_material-9.5.6-py3-none-any.whl", hash = "sha256:e115b90fccf5cd7f5d15b0c2f8e6246b21041628b8f590630e7fca66ed7fcf6c"}, + {file = "mkdocs_material-9.5.6.tar.gz", hash = "sha256:5b24df36d8ac6cecd611241ce6f6423ccde3e1ad89f8360c3f76d5565fc2d82a"}, ] [[package]] @@ -942,7 +976,7 @@ files = [ [[package]] name = "mkdocstrings-python" -version = "1.7.5" +version = "1.8.0" requires_python = ">=3.8" summary = "A Python handler for mkdocstrings." dependencies = [ @@ -950,8 +984,8 @@ dependencies = [ "mkdocstrings>=0.20", ] files = [ - {file = "mkdocstrings_python-1.7.5-py3-none-any.whl", hash = "sha256:5f6246026353f0c0785135db70c3fe9a5d9318990fc7ceb11d62097b8ffdd704"}, - {file = "mkdocstrings_python-1.7.5.tar.gz", hash = "sha256:c7d143728257dbf1aa550446555a554b760dcd40a763f077189d298502b800be"}, + {file = "mkdocstrings_python-1.8.0-py3-none-any.whl", hash = "sha256:4209970cc90bec194568682a535848a8d8489516c6ed4adbe58bbc67b699ca9d"}, + {file = "mkdocstrings_python-1.8.0.tar.gz", hash = "sha256:1488bddf50ee42c07d9a488dddc197f8e8999c2899687043ec5dd1643d057192"}, ] [[package]] @@ -1032,23 +1066,24 @@ files = [ [[package]] name = "pandas" -version = "2.1.4" +version = "2.2.0" requires_python = ">=3.9" summary = "Powerful data structures for data analysis, time series, and statistics" dependencies = [ "numpy<2,>=1.23.2; python_version == \"3.11\"", "python-dateutil>=2.8.2", "pytz>=2020.1", - "tzdata>=2022.1", + "tzdata>=2022.7", ] files = [ - {file = "pandas-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b7d852d16c270e4331f6f59b3e9aa23f935f5c4b0ed2d0bc77637a8890a5d092"}, - {file = "pandas-2.1.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd7d5f2f54f78164b3d7a40f33bf79a74cdee72c31affec86bfcabe7e0789821"}, - {file = "pandas-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0aa6e92e639da0d6e2017d9ccff563222f4eb31e4b2c3cf32a2a392fc3103c0d"}, - {file = "pandas-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d797591b6846b9db79e65dc2d0d48e61f7db8d10b2a9480b4e3faaddc421a171"}, - {file = "pandas-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2d3e7b00f703aea3945995ee63375c61b2e6aa5aa7871c5d622870e5e137623"}, - {file = "pandas-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:dc9bf7ade01143cddc0074aa6995edd05323974e6e40d9dbde081021ded8510e"}, - {file = "pandas-2.1.4.tar.gz", hash = "sha256:fcb68203c833cc735321512e13861358079a96c174a61f5116a1de89c58c0ef7"}, + {file = "pandas-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a1b438fa26b208005c997e78672f1aa8138f67002e833312e6230f3e57fa87d5"}, + {file = "pandas-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ce2fbc8d9bf303ce54a476116165220a1fedf15985b09656b4b4275300e920b"}, + {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2707514a7bec41a4ab81f2ccce8b382961a29fbe9492eab1305bb075b2b1ff4f"}, + {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85793cbdc2d5bc32620dc8ffa715423f0c680dacacf55056ba13454a5be5de88"}, + {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cfd6c2491dc821b10c716ad6776e7ab311f7df5d16038d0b7458bc0b67dc10f3"}, + {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a146b9dcacc3123aa2b399df1a284de5f46287a4ab4fbfc237eac98a92ebcb71"}, + {file = "pandas-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbc1b53c0e1fdf16388c33c3cca160f798d38aea2978004dd3f4d3dec56454c9"}, + {file = "pandas-2.2.0.tar.gz", hash = "sha256:30b83f7c3eb217fb4d1b494a57a2fda5444f17834f5df2de6b2ffff68dc3c8e2"}, ] [[package]] @@ -1119,22 +1154,22 @@ files = [ [[package]] name = "platformdirs" -version = "4.1.0" +version = "4.2.0" requires_python = ">=3.8" summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." files = [ - {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, - {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" requires_python = ">=3.8" summary = "plugin and hook calling mechanisms for python" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [[package]] @@ -1180,72 +1215,58 @@ files = [ [[package]] name = "pydantic" -version = "2.5.3" -requires_python = ">=3.7" +version = "2.6.0" +requires_python = ">=3.8" summary = "Data validation using Python type hints" dependencies = [ "annotated-types>=0.4.0", - "pydantic-core==2.14.6", + "pydantic-core==2.16.1", "typing-extensions>=4.6.1", ] files = [ - {file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"}, - {file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"}, + {file = "pydantic-2.6.0-py3-none-any.whl", hash = "sha256:1440966574e1b5b99cf75a13bec7b20e3512e8a61b894ae252f56275e2c465ae"}, + {file = "pydantic-2.6.0.tar.gz", hash = "sha256:ae887bd94eb404b09d86e4d12f93893bdca79d766e738528c6fa1c849f3c6bcf"}, ] [[package]] name = "pydantic-core" -version = "2.14.6" -requires_python = ">=3.7" +version = "2.16.1" +requires_python = ">=3.8" summary = "" dependencies = [ "typing-extensions!=4.7.0,>=4.6.0", ] files = [ - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"}, - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"}, - {file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"}, - {file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"}, - {file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"}, - {file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"}, + {file = "pydantic_core-2.16.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:102569d371fadc40d8f8598a59379c37ec60164315884467052830b28cc4e9da"}, + {file = "pydantic_core-2.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:735dceec50fa907a3c314b84ed609dec54b76a814aa14eb90da31d1d36873a5e"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e83ebbf020be727d6e0991c1b192a5c2e7113eb66e3def0cd0c62f9f266247e4"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:30a8259569fbeec49cfac7fda3ec8123486ef1b729225222f0d41d5f840b476f"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920c4897e55e2881db6a6da151198e5001552c3777cd42b8a4c2f72eedc2ee91"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5247a3d74355f8b1d780d0f3b32a23dd9f6d3ff43ef2037c6dcd249f35ecf4c"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5bea8012df5bb6dda1e67d0563ac50b7f64a5d5858348b5c8cb5043811c19d"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ed3025a8a7e5a59817b7494686d449ebfbe301f3e757b852c8d0d1961d6be864"}, + {file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:06f0d5a1d9e1b7932477c172cc720b3b23c18762ed7a8efa8398298a59d177c7"}, + {file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:150ba5c86f502c040b822777e2e519b5625b47813bd05f9273a8ed169c97d9ae"}, + {file = "pydantic_core-2.16.1-cp311-none-win32.whl", hash = "sha256:d6cbdf12ef967a6aa401cf5cdf47850559e59eedad10e781471c960583f25aa1"}, + {file = "pydantic_core-2.16.1-cp311-none-win_amd64.whl", hash = "sha256:afa01d25769af33a8dac0d905d5c7bb2d73c7c3d5161b2dd6f8b5b5eea6a3c4c"}, + {file = "pydantic_core-2.16.1-cp311-none-win_arm64.whl", hash = "sha256:1a2fe7b00a49b51047334d84aafd7e39f80b7675cad0083678c58983662da89b"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d25ef0c33f22649b7a088035fd65ac1ce6464fa2876578df1adad9472f918a76"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99c095457eea8550c9fa9a7a992e842aeae1429dab6b6b378710f62bfb70b394"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b49c604ace7a7aa8af31196abbf8f2193be605db6739ed905ecaf62af31ccae0"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c56da23034fe66221f2208c813d8aa509eea34d97328ce2add56e219c3a9f41c"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cebf8d56fee3b08ad40d332a807ecccd4153d3f1ba8231e111d9759f02edfd05"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1ae8048cba95f382dba56766525abca438328455e35c283bb202964f41a780b0"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:780daad9e35b18d10d7219d24bfb30148ca2afc309928e1d4d53de86822593dc"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c94b5537bf6ce66e4d7830c6993152940a188600f6ae044435287753044a8fe2"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:adf28099d061a25fbcc6531febb7a091e027605385de9fe14dd6a97319d614cf"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:644904600c15816a1f9a1bafa6aab0d21db2788abcdf4e2a77951280473f33e1"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87bce04f09f0552b66fca0c4e10da78d17cb0e71c205864bab4e9595122cb9d9"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:877045a7969ace04d59516d5d6a7dee13106822f99a5d8df5e6822941f7bedc8"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9c46e556ee266ed3fb7b7a882b53df3c76b45e872fdab8d9cf49ae5e91147fd7"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4eebbd049008eb800f519578e944b8dc8e0f7d59a5abb5924cc2d4ed3a1834ff"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c0be58529d43d38ae849a91932391eb93275a06b93b79a8ab828b012e916a206"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b1fc07896fc1851558f532dffc8987e526b682ec73140886c831d773cef44b76"}, + {file = "pydantic_core-2.16.1.tar.gz", hash = "sha256:daff04257b49ab7f4b3f73f98283d3dbb1a65bf3500d55c7beac3c66c310fe34"}, ] [[package]] @@ -1346,15 +1367,15 @@ files = [ [[package]] name = "pyright" -version = "1.1.344" +version = "1.1.349" requires_python = ">=3.7" summary = "Command line wrapper for pyright" dependencies = [ "nodeenv>=1.6.0", ] files = [ - {file = "pyright-1.1.344-py3-none-any.whl", hash = "sha256:ab7117a911ce25fcd317f42272579f9ae53a6abc8b8a15f6aa069a11281953ee"}, - {file = "pyright-1.1.344.tar.gz", hash = "sha256:ab7c962f00dd8141a5a0192c1060fb34b92d1f9047ad70dda45229938051922b"}, + {file = "pyright-1.1.349-py3-none-any.whl", hash = "sha256:8f9189ddb62222a35b3525666225f1d8f24244cbff5893c42b3f001d8ebafa1a"}, + {file = "pyright-1.1.349.tar.gz", hash = "sha256:af4ab7f103a0b2a92e5fbf248bf734e9a98247991350ac989ead34e97148f91c"}, ] [[package]] @@ -1375,15 +1396,29 @@ files = [ [[package]] name = "pytest-asyncio" -version = "0.23.3" +version = "0.23.4" requires_python = ">=3.8" summary = "Pytest support for asyncio" dependencies = [ - "pytest>=7.0.0", + "pytest<8,>=7.0.0", ] files = [ - {file = "pytest-asyncio-0.23.3.tar.gz", hash = "sha256:af313ce900a62fbe2b1aed18e37ad757f1ef9940c6b6a88e2954de38d6b1fb9f"}, - {file = "pytest_asyncio-0.23.3-py3-none-any.whl", hash = "sha256:37a9d912e8338ee7b4a3e917381d1c95bfc8682048cb0fbc35baba316ec1faba"}, + {file = "pytest-asyncio-0.23.4.tar.gz", hash = "sha256:2143d9d9375bf372a73260e4114541485e84fca350b0b6b92674ca56ff5f7ea2"}, + {file = "pytest_asyncio-0.23.4-py3-none-any.whl", hash = "sha256:b0079dfac14b60cd1ce4691fbfb1748fe939db7d0234b5aba97197d10fbe0fef"}, +] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +requires_python = ">=3.7" +summary = "Pytest plugin for measuring coverage." +dependencies = [ + "coverage[toml]>=5.2.1", + "pytest>=4.6", +] +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, ] [[package]] @@ -1415,12 +1450,12 @@ files = [ [[package]] name = "python-dotenv" -version = "1.0.0" +version = "1.0.1" requires_python = ">=3.8" summary = "Read key-value pairs from a .env file and set them as environment variables" files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, ] [[package]] @@ -1435,11 +1470,11 @@ files = [ [[package]] name = "pytz" -version = "2023.3.post1" +version = "2023.4" summary = "World timezone definitions, modern and historical" files = [ - {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, - {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, + {file = "pytz-2023.4-py2.py3-none-any.whl", hash = "sha256:f90ef520d95e7c46951105338d918664ebfd6f1d995bd7d153127ce90efafa6a"}, + {file = "pytz-2023.4.tar.gz", hash = "sha256:31d4583c4ed539cd037956140d695e42c033a19e984bfce9964a3f7d59bc2b40"}, ] [[package]] @@ -1514,65 +1549,70 @@ files = [ [[package]] name = "ruff" -version = "0.1.11" +version = "0.1.15" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." files = [ - {file = "ruff-0.1.11-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a7f772696b4cdc0a3b2e527fc3c7ccc41cdcb98f5c80fdd4f2b8c50eb1458196"}, - {file = "ruff-0.1.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:934832f6ed9b34a7d5feea58972635c2039c7a3b434fe5ba2ce015064cb6e955"}, - {file = "ruff-0.1.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea0d3e950e394c4b332bcdd112aa566010a9f9c95814844a7468325290aabfd9"}, - {file = "ruff-0.1.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9bd4025b9c5b429a48280785a2b71d479798a69f5c2919e7d274c5f4b32c3607"}, - {file = "ruff-0.1.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1ad00662305dcb1e987f5ec214d31f7d6a062cae3e74c1cbccef15afd96611d"}, - {file = "ruff-0.1.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4b077ce83f47dd6bea1991af08b140e8b8339f0ba8cb9b7a484c30ebab18a23f"}, - {file = "ruff-0.1.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a88efecec23c37b11076fe676e15c6cdb1271a38f2b415e381e87fe4517f18"}, - {file = "ruff-0.1.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b25093dad3b055667730a9b491129c42d45e11cdb7043b702e97125bcec48a1"}, - {file = "ruff-0.1.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:231d8fb11b2cc7c0366a326a66dafc6ad449d7fcdbc268497ee47e1334f66f77"}, - {file = "ruff-0.1.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:09c415716884950080921dd6237767e52e227e397e2008e2bed410117679975b"}, - {file = "ruff-0.1.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0f58948c6d212a6b8d41cd59e349751018797ce1727f961c2fa755ad6208ba45"}, - {file = "ruff-0.1.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:190a566c8f766c37074d99640cd9ca3da11d8deae2deae7c9505e68a4a30f740"}, - {file = "ruff-0.1.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6464289bd67b2344d2a5d9158d5eb81025258f169e69a46b741b396ffb0cda95"}, - {file = "ruff-0.1.11-py3-none-win32.whl", hash = "sha256:9b8f397902f92bc2e70fb6bebfa2139008dc72ae5177e66c383fa5426cb0bf2c"}, - {file = "ruff-0.1.11-py3-none-win_amd64.whl", hash = "sha256:eb85ee287b11f901037a6683b2374bb0ec82928c5cbc984f575d0437979c521a"}, - {file = "ruff-0.1.11-py3-none-win_arm64.whl", hash = "sha256:97ce4d752f964ba559c7023a86e5f8e97f026d511e48013987623915431c7ea9"}, - {file = "ruff-0.1.11.tar.gz", hash = "sha256:f9d4d88cb6eeb4dfe20f9f0519bd2eaba8119bde87c3d5065c541dbae2b5a2cb"}, + {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5fe8d54df166ecc24106db7dd6a68d44852d14eb0729ea4672bb4d96c320b7df"}, + {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f0bfbb53c4b4de117ac4d6ddfd33aa5fc31beeaa21d23c45c6dd249faf9126f"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d432aec35bfc0d800d4f70eba26e23a352386be3a6cf157083d18f6f5881c8"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9405fa9ac0e97f35aaddf185a1be194a589424b8713e3b97b762336ec79ff807"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66ec24fe36841636e814b8f90f572a8c0cb0e54d8b5c2d0e300d28a0d7bffec"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6f8ad828f01e8dd32cc58bc28375150171d198491fc901f6f98d2a39ba8e3ff5"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86811954eec63e9ea162af0ffa9f8d09088bab51b7438e8b6488b9401863c25e"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4025ac5e87d9b80e1f300207eb2fd099ff8200fa2320d7dc066a3f4622dc6b"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b17b93c02cdb6aeb696effecea1095ac93f3884a49a554a9afa76bb125c114c1"}, + {file = "ruff-0.1.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ddb87643be40f034e97e97f5bc2ef7ce39de20e34608f3f829db727a93fb82c5"}, + {file = "ruff-0.1.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:abf4822129ed3a5ce54383d5f0e964e7fef74a41e48eb1dfad404151efc130a2"}, + {file = "ruff-0.1.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c629cf64bacfd136c07c78ac10a54578ec9d1bd2a9d395efbee0935868bf852"}, + {file = "ruff-0.1.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1bab866aafb53da39c2cadfb8e1c4550ac5340bb40300083eb8967ba25481447"}, + {file = "ruff-0.1.15-py3-none-win32.whl", hash = "sha256:2417e1cb6e2068389b07e6fa74c306b2810fe3ee3476d5b8a96616633f40d14f"}, + {file = "ruff-0.1.15-py3-none-win_amd64.whl", hash = "sha256:3837ac73d869efc4182d9036b1405ef4c73d9b1f88da2413875e34e0d6919587"}, + {file = "ruff-0.1.15-py3-none-win_arm64.whl", hash = "sha256:9a933dfb1c14ec7a33cceb1e49ec4a16b51ce3c20fd42663198746efc0427360"}, + {file = "ruff-0.1.15.tar.gz", hash = "sha256:f6dfa8c1b21c913c326919056c390966648b680966febcb796cc9d1aaab8564e"}, ] [[package]] name = "scikit-learn" -version = "1.3.2" -requires_python = ">=3.8" +version = "1.4.0" +requires_python = ">=3.9" summary = "A set of python modules for machine learning and data mining" dependencies = [ - "joblib>=1.1.1", - "numpy<2.0,>=1.17.3", - "scipy>=1.5.0", + "joblib>=1.2.0", + "numpy<2.0,>=1.19.5", + "scipy>=1.6.0", "threadpoolctl>=2.0.0", ] files = [ - {file = "scikit-learn-1.3.2.tar.gz", hash = "sha256:a2f54c76accc15a34bfb9066e6c7a56c1e7235dda5762b990792330b52ccfb05"}, - {file = "scikit_learn-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6fb6bc98f234fda43163ddbe36df8bcde1d13ee176c6dc9b92bb7d3fc842eb66"}, - {file = "scikit_learn-1.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:18424efee518a1cde7b0b53a422cde2f6625197de6af36da0b57ec502f126157"}, - {file = "scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3271552a5eb16f208a6f7f617b8cc6d1f137b52c8a1ef8edf547db0259b2c9fb"}, - {file = "scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4144a5004a676d5022b798d9e573b05139e77f271253a4703eed295bde0433"}, - {file = "scikit_learn-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:67f37d708f042a9b8d59551cf94d30431e01374e00dc2645fa186059c6c5d78b"}, + {file = "scikit-learn-1.4.0.tar.gz", hash = "sha256:d4373c984eba20e393216edd51a3e3eede56cbe93d4247516d205643c3b93121"}, + {file = "scikit_learn-1.4.0-1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8341eabdc754d5ab91641a7763243845e96b6d68e03e472531e88a4f1b09f21"}, + {file = "scikit_learn-1.4.0-1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d1f6bce875ac2bb6b52514f67c185c564ccd299a05b65b7bab091a4c13dde12d"}, + {file = "scikit_learn-1.4.0-1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c408b46b2fd61952d519ea1af2f8f0a7a703e1433923ab1704c4131520b2083b"}, + {file = "scikit_learn-1.4.0-1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b465dd1dcd237b7b1dcd1a9048ccbf70a98c659474324fa708464c3a2533fad"}, + {file = "scikit_learn-1.4.0-1-cp311-cp311-win_amd64.whl", hash = "sha256:0db8e22c42f7980fe5eb22069b1f84c48966f3e0d23a01afde5999e3987a2501"}, + {file = "scikit_learn-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27ae4b0f1b2c77107c096a7e05b33458354107b47775428d1f11b23e30a73e8a"}, + {file = "scikit_learn-1.4.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5c5c62ffb52c3ffb755eb21fa74cc2cbf2c521bd53f5c04eaa10011dbecf5f80"}, + {file = "scikit_learn-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f0d2018ac6fa055dab65fe8a485967990d33c672d55bc254c56c35287b02fab"}, + {file = "scikit_learn-1.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a8918c415c4b4bf1d60c38d32958849a9191c2428ab35d30b78354085c7c7a"}, + {file = "scikit_learn-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:80a21de63275f8bcd7877b3e781679d2ff1eddfed515a599f95b2502a3283d42"}, ] [[package]] name = "scipy" -version = "1.11.4" +version = "1.12.0" requires_python = ">=3.9" summary = "Fundamental algorithms for scientific computing in Python" dependencies = [ - "numpy<1.28.0,>=1.21.6", + "numpy<1.29.0,>=1.22.4", ] files = [ - {file = "scipy-1.11.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f313b39a7e94f296025e3cffc2c567618174c0b1dde173960cf23808f9fae4be"}, - {file = "scipy-1.11.4-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1b7c3dca977f30a739e0409fb001056484661cb2541a01aba0bb0029f7b68db8"}, - {file = "scipy-1.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00150c5eae7b610c32589dda259eacc7c4f1665aedf25d921907f4d08a951b1c"}, - {file = "scipy-1.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530f9ad26440e85766509dbf78edcfe13ffd0ab7fec2560ee5c36ff74d6269ff"}, - {file = "scipy-1.11.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5e347b14fe01003d3b78e196e84bd3f48ffe4c8a7b8a1afbcb8f5505cb710993"}, - {file = "scipy-1.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:acf8ed278cc03f5aff035e69cb511741e0418681d25fbbb86ca65429c4f4d9cd"}, - {file = "scipy-1.11.4.tar.gz", hash = "sha256:90a2b78e7f5733b9de748f589f09225013685f9b218275257f8a8168ededaeaa"}, + {file = "scipy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:408c68423f9de16cb9e602528be4ce0d6312b05001f3de61fe9ec8b1263cad08"}, + {file = "scipy-1.12.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5adfad5dbf0163397beb4aca679187d24aec085343755fcdbdeb32b3679f254c"}, + {file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3003652496f6e7c387b1cf63f4bb720951cfa18907e998ea551e6de51a04467"}, + {file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b8066bce124ee5531d12a74b617d9ac0ea59245246410e19bca549656d9a40a"}, + {file = "scipy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8bee4993817e204d761dba10dbab0774ba5a8612e57e81319ea04d84945375ba"}, + {file = "scipy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a24024d45ce9a675c1fb8494e8e5244efea1c7a09c60beb1eeb80373d0fecc70"}, + {file = "scipy-1.12.0.tar.gz", hash = "sha256:4bf5abab8a36d20193c698b0f1fc282c1d083c94723902c447e5d2f1780936a3"}, ] [[package]] @@ -1743,7 +1783,7 @@ version = "2.0.25" requires_python = ">=3.7" summary = "Database Abstraction Library" dependencies = [ - "greenlet!=0.4.17; platform_machine == \"aarch64\" or (platform_machine == \"ppc64le\" or (platform_machine == \"x86_64\" or (platform_machine == \"amd64\" or (platform_machine == \"AMD64\" or (platform_machine == \"win32\" or platform_machine == \"WIN32\")))))", + "greenlet!=0.4.17; platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\"", "typing-extensions>=4.6.0", ] files = [ @@ -1784,15 +1824,15 @@ files = [ [[package]] name = "starlette" -version = "0.32.0.post1" +version = "0.35.1" requires_python = ">=3.8" summary = "The little ASGI library that shines." dependencies = [ "anyio<5,>=3.4.0", ] files = [ - {file = "starlette-0.32.0.post1-py3-none-any.whl", hash = "sha256:cd0cb10ddb49313f609cedfac62c8c12e56c7314b66d89bb077ba228bada1b09"}, - {file = "starlette-0.32.0.post1.tar.gz", hash = "sha256:e54e2b7e2fb06dff9eac40133583f10dfa05913f5a85bf26f427c7a40a9a3d02"}, + {file = "starlette-0.35.1-py3-none-any.whl", hash = "sha256:50bbbda9baa098e361f398fda0928062abbaf1f54f4fadcbe17c092a01eb9a25"}, + {file = "starlette-0.35.1.tar.gz", hash = "sha256:3e2639dac3520e4f58734ed22553f950d3f3cb1001cd2eaac4d57e8cdc5f66bc"}, ] [[package]] @@ -1807,23 +1847,23 @@ files = [ [[package]] name = "tox" -version = "4.11.4" +version = "4.12.1" requires_python = ">=3.8" summary = "tox is a generic virtualenv management and test command line tool" dependencies = [ - "cachetools>=5.3.1", + "cachetools>=5.3.2", "chardet>=5.2", "colorama>=0.4.6", - "filelock>=3.12.3", - "packaging>=23.1", - "platformdirs>=3.10", + "filelock>=3.13.1", + "packaging>=23.2", + "platformdirs>=4.1", "pluggy>=1.3", "pyproject-api>=1.6.1", - "virtualenv>=20.24.3", + "virtualenv>=20.25", ] files = [ - {file = "tox-4.11.4-py3-none-any.whl", hash = "sha256:2adb83d68f27116812b69aa36676a8d6a52249cb0d173649de0e7d0c2e3e7229"}, - {file = "tox-4.11.4.tar.gz", hash = "sha256:73a7240778fabf305aeb05ab8ea26e575e042ab5a18d71d0ed13e343a51d6ce1"}, + {file = "tox-4.12.1-py3-none-any.whl", hash = "sha256:c07ea797880a44f3c4f200ad88ad92b446b83079d4ccef89585df64cc574375c"}, + {file = "tox-4.12.1.tar.gz", hash = "sha256:61aafbeff1bd8a5af84e54ef6e8402f53c6a6066d0782336171ddfbf5362122e"}, ] [[package]] @@ -1848,17 +1888,17 @@ files = [ [[package]] name = "urllib3" -version = "2.1.0" +version = "2.2.0" requires_python = ">=3.8" summary = "HTTP library with thread-safe connection pooling, file post, and more." files = [ - {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, - {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, + {file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"}, + {file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"}, ] [[package]] name = "uvicorn" -version = "0.25.0" +version = "0.27.0.post1" requires_python = ">=3.8" summary = "The lightning-fast ASGI server." dependencies = [ @@ -1866,13 +1906,13 @@ dependencies = [ "h11>=0.8", ] files = [ - {file = "uvicorn-0.25.0-py3-none-any.whl", hash = "sha256:ce107f5d9bd02b4636001a77a4e74aab5e1e2b146868ebbad565237145af444c"}, - {file = "uvicorn-0.25.0.tar.gz", hash = "sha256:6dddbad1d7ee0f5140aba5ec138ddc9612c5109399903828b4874c9937f009c2"}, + {file = "uvicorn-0.27.0.post1-py3-none-any.whl", hash = "sha256:4b85ba02b8a20429b9b205d015cbeb788a12da527f731811b643fd739ef90d5f"}, + {file = "uvicorn-0.27.0.post1.tar.gz", hash = "sha256:54898fcd80c13ff1cd28bf77b04ec9dbd8ff60c5259b499b4b12bb0917f22907"}, ] [[package]] name = "uvicorn" -version = "0.25.0" +version = "0.27.0.post1" extras = ["standard"] requires_python = ">=3.8" summary = "The lightning-fast ASGI server." @@ -1881,14 +1921,14 @@ dependencies = [ "httptools>=0.5.0", "python-dotenv>=0.13", "pyyaml>=5.1", - "uvicorn==0.25.0", - "uvloop!=0.15.0,!=0.15.1,>=0.14.0; sys_platform != \"win32\" and (sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\")", + "uvicorn==0.27.0.post1", + "uvloop!=0.15.0,!=0.15.1,>=0.14.0; (sys_platform != \"cygwin\" and sys_platform != \"win32\") and platform_python_implementation != \"PyPy\"", "watchfiles>=0.13", "websockets>=10.4", ] files = [ - {file = "uvicorn-0.25.0-py3-none-any.whl", hash = "sha256:ce107f5d9bd02b4636001a77a4e74aab5e1e2b146868ebbad565237145af444c"}, - {file = "uvicorn-0.25.0.tar.gz", hash = "sha256:6dddbad1d7ee0f5140aba5ec138ddc9612c5109399903828b4874c9937f009c2"}, + {file = "uvicorn-0.27.0.post1-py3-none-any.whl", hash = "sha256:4b85ba02b8a20429b9b205d015cbeb788a12da527f731811b643fd739ef90d5f"}, + {file = "uvicorn-0.27.0.post1.tar.gz", hash = "sha256:54898fcd80c13ff1cd28bf77b04ec9dbd8ff60c5259b499b4b12bb0917f22907"}, ] [[package]] @@ -2021,15 +2061,15 @@ files = [ [[package]] name = "xarray" -version = "2023.12.0" +version = "2024.1.1" requires_python = ">=3.9" summary = "N-D labeled arrays and datasets in Python" dependencies = [ - "numpy>=1.22", - "packaging>=21.3", - "pandas>=1.4", + "numpy>=1.23", + "packaging>=22", + "pandas>=1.5", ] files = [ - {file = "xarray-2023.12.0-py3-none-any.whl", hash = "sha256:3c22b6824681762b6c3fcad86dfd18960a617bccbc7f456ce21b43a20e455fb9"}, - {file = "xarray-2023.12.0.tar.gz", hash = "sha256:4565dbc890de47e278346c44d6b33bb07d3427383e077a7ca8ab6606196fd433"}, + {file = "xarray-2024.1.1-py3-none-any.whl", hash = "sha256:0bec81303b088c8df4f075e1579c00cfd7e5069688e4434007f0b8d7df17fc1c"}, + {file = "xarray-2024.1.1.tar.gz", hash = "sha256:a1ba2d87a74892e213c9c83f4a462dbcdf68212320a4e31b34bd789ba7a64e35"}, ] diff --git a/back/pyproject.toml b/back/pyproject.toml index d11d8695..d48020b1 100644 --- a/back/pyproject.toml +++ b/back/pyproject.toml @@ -84,4 +84,5 @@ dev = [ "mkdocs>=1.5.3", "mkdocs-material>=9.4.14", "mkdocstrings[python]>=0.24.0", + "pytest-cov>=4.1.0", ] From 1fe5f7b5e7c14d1a65b1513a9fbf63717716bc4f Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 21:33:50 +0000 Subject: [PATCH 16/20] Fix dockerfile build --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index b322c2d5..ae7451e0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -59,6 +59,7 @@ COPY back/app.py /code/app.py COPY back/pyproject.toml /code/pyproject.toml COPY back/alembic.ini /code/alembic.ini COPY back/README.md /code/README.md +COPY back/LICENSE /code/LICENSE # Install Whombat RUN pip install --no-deps . From 21cf30b95a62156f7d41df11258a8dc089a67882 Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 21:34:46 +0000 Subject: [PATCH 17/20] Update test workflow --- .github/workflows/test.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 174b7eba..a37fa7db 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,9 +24,7 @@ jobs: - name: Run tests run: | cd back - pdm run coverage run --source whombat -m pytest - pdm run coverage report -m - pdm run coverage html + pdm run pytest --cov -n auto - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 env: From 5a13967d106e37cc7df20f4f19afed8429546f6f Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Wed, 31 Jan 2024 21:50:25 +0000 Subject: [PATCH 18/20] Better build metadata? --- .gitignore | 9 ++++----- back/pyproject.toml | 8 +++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index e94dabcc..94c99385 100644 --- a/.gitignore +++ b/.gitignore @@ -166,12 +166,11 @@ front/src/old_hooks old site +back/user_guide whombat.db .pdm-python -back/src/whombat/statics -back/src/whombat/statics +back/src/whombat/statics/* +back/src/whombat/user_guide/* back/.build_venv -back/user_guide -back/src/whombat/user_guide -.bumpversion.cfg \ No newline at end of file +.bumpversion.cfg diff --git a/back/pyproject.toml b/back/pyproject.toml index d48020b1..39dba52c 100644 --- a/back/pyproject.toml +++ b/back/pyproject.toml @@ -41,9 +41,11 @@ postgre = ["asyncpg>=0.29.0", "psycopg2-binary>=2.9.9"] requires = ["hatchling"] build-backend = "hatchling.build" -[tool.hatch.build.targets.wheel.force-include] -"src/whombat/migrations/versions/" = "whombat/migrations/versions/" -"src/whombat/statics/" = "whombat/statics/" +[tool.hatch.build.targets.wheel] +artifacts = [ + "src/whombat/migrations/versions/", + "src/whombat/statics/", +] [tool.black] line-length = 79 From dece2d1262b3978870d976a3899232f53e94986e Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Thu, 1 Feb 2024 08:40:36 +0000 Subject: [PATCH 19/20] Fixed auth issue --- back/src/whombat/api/__init__.py | 1 + back/src/whombat/api/audio.py | 1 + back/src/whombat/api/clip_evaluations.py | 1 + back/src/whombat/api/datasets.py | 1 + back/src/whombat/api/evaluation_sets.py | 1 + back/src/whombat/api/io/aoef/clips.py | 10 +- back/src/whombat/api/io/aoef/features.py | 10 +- back/src/whombat/api/model_runs.py | 1 + back/src/whombat/api/recordings.py | 1 + back/src/whombat/api/sessions.py | 1 + .../whombat/api/sound_event_annotations.py | 8 +- .../whombat/api/sound_event_evaluations.py | 1 + back/src/whombat/api/sound_events.py | 8 +- back/src/whombat/api/spectrograms.py | 1 + back/src/whombat/api/tags.py | 1 + back/src/whombat/api/users.py | 1 + back/src/whombat/app.py | 1 + back/src/whombat/cache.py | 1 + back/src/whombat/core/files.py | 1 + back/src/whombat/exceptions.py | 1 - back/src/whombat/filters/annotation_tasks.py | 1 + back/src/whombat/filters/clip_predictions.py | 1 + back/src/whombat/filters/evaluations.py | 1 + back/src/whombat/filters/feature_names.py | 1 + back/src/whombat/filters/notes.py | 1 + .../filters/sound_event_annotations.py | 1 + .../filters/sound_event_predictions.py | 1 + back/src/whombat/filters/sound_events.py | 1 + back/src/whombat/filters/tags.py | 1 + .../versions/03a1d239e06e_first_migration.py | 1 + back/src/whombat/models/__init__.py | 1 + back/src/whombat/models/annotation_project.py | 12 +- back/src/whombat/models/annotation_task.py | 18 +- back/src/whombat/models/clip_annotation.py | 46 +- back/src/whombat/models/clip_evaluation.py | 18 +- back/src/whombat/models/dataset.py | 18 +- back/src/whombat/models/evaluation_set.py | 49 +- back/src/whombat/models/model_run.py | 40 +- back/src/whombat/models/note.py | 36 +- back/src/whombat/models/recording.py | 16 +- back/src/whombat/models/sound_event.py | 24 +- .../whombat/models/sound_event_annotation.py | 25 +- .../whombat/models/sound_event_evaluation.py | 12 +- .../whombat/models/sound_event_prediction.py | 1 + back/src/whombat/models/tag.py | 93 ++-- back/src/whombat/models/token.py | 1 + back/src/whombat/models/user.py | 1 + back/src/whombat/models/user_run.py | 38 +- back/src/whombat/routes/__init__.py | 18 +- .../src/whombat/routes/annotation_projects.py | 1 + back/src/whombat/routes/annotation_tasks.py | 328 ++++++------ back/src/whombat/routes/audio.py | 1 + back/src/whombat/routes/auth.py | 1 + back/src/whombat/routes/clip_annotations.py | 367 +++++++------- back/src/whombat/routes/clip_evaluations.py | 1 + back/src/whombat/routes/clip_predictions.py | 1 + back/src/whombat/routes/clips.py | 1 + back/src/whombat/routes/datasets.py | 1 + .../whombat/routes/dependencies/__init__.py | 5 +- back/src/whombat/routes/dependencies/auth.py | 32 +- .../whombat/routes/dependencies/session.py | 1 + .../whombat/routes/dependencies/settings.py | 1 + back/src/whombat/routes/dependencies/users.py | 1 + back/src/whombat/routes/evaluation_sets.py | 1 + back/src/whombat/routes/evaluations.py | 1 + back/src/whombat/routes/model_runs.py | 1 + back/src/whombat/routes/notes.py | 1 + back/src/whombat/routes/plugins.py | 1 + back/src/whombat/routes/recordings.py | 437 ++++++++-------- .../whombat/routes/sound_event_annotations.py | 476 +++++++++--------- .../whombat/routes/sound_event_evaluations.py | 1 + .../whombat/routes/sound_event_predictions.py | 1 + back/src/whombat/routes/sound_events.py | 1 + back/src/whombat/routes/spectrograms.py | 1 + back/src/whombat/routes/tags.py | 1 + back/src/whombat/routes/types.py | 1 + back/src/whombat/routes/user_runs.py | 111 ++-- back/src/whombat/routes/users.py | 1 + back/src/whombat/schemas/evaluations.py | 1 + back/src/whombat/schemas/model_runs.py | 1 + back/src/whombat/schemas/plugin.py | 1 - back/src/whombat/schemas/tags.py | 1 + back/src/whombat/schemas/user_runs.py | 1 + back/src/whombat/schemas/users.py | 6 +- back/src/whombat/system/app.py | 1 + back/src/whombat/system/auth.py | 16 +- back/src/whombat/system/data.py | 1 + back/src/whombat/system/database.py | 1 + back/src/whombat/system/settings.py | 1 + back/src/whombat/system/users.py | 1 + back/tests/conftest.py | 4 +- .../test_api/test_annotation_projects.py | 1 + back/tests/test_api/test_clips.py | 1 + back/tests/test_api/test_notes.py | 1 + back/tests/test_api/test_recordings.py | 1 + back/tests/test_api/test_sound_events.py | 1 + back/tests/test_api/test_users.py | 1 + back/tests/test_core/test_files.py | 1 + back/tests/test_database/test_database.py | 1 + .../test_filters/test_recording_filters.py | 1 + back/tests/test_routers/conftest.py | 21 +- back/tests/test_routers/test_auth.py | 15 + .../test_sound_event_annotations.py | 26 + 103 files changed, 1292 insertions(+), 1122 deletions(-) create mode 100644 back/tests/test_routers/test_sound_event_annotations.py diff --git a/back/src/whombat/api/__init__.py b/back/src/whombat/api/__init__.py index 30ed37a1..1ae3a47f 100644 --- a/back/src/whombat/api/__init__.py +++ b/back/src/whombat/api/__init__.py @@ -1,4 +1,5 @@ """Python API for Whombat.""" + from whombat.api.annotation_projects import annotation_projects from whombat.api.annotation_tasks import annotation_tasks from whombat.api.audio import load_audio, load_clip_bytes diff --git a/back/src/whombat/api/audio.py b/back/src/whombat/api/audio.py index 373432bf..881dbd86 100644 --- a/back/src/whombat/api/audio.py +++ b/back/src/whombat/api/audio.py @@ -1,4 +1,5 @@ """API functions to load audio.""" + import struct from pathlib import Path diff --git a/back/src/whombat/api/clip_evaluations.py b/back/src/whombat/api/clip_evaluations.py index 8d73636e..da7a0857 100644 --- a/back/src/whombat/api/clip_evaluations.py +++ b/back/src/whombat/api/clip_evaluations.py @@ -1,4 +1,5 @@ """API functions to interact with clip evaluations.""" + from pathlib import Path from typing import Sequence from uuid import UUID diff --git a/back/src/whombat/api/datasets.py b/back/src/whombat/api/datasets.py index 3db3c937..cdf535e9 100644 --- a/back/src/whombat/api/datasets.py +++ b/back/src/whombat/api/datasets.py @@ -1,4 +1,5 @@ """API functions for interacting with datasets.""" + import datetime import uuid import warnings diff --git a/back/src/whombat/api/evaluation_sets.py b/back/src/whombat/api/evaluation_sets.py index 0fe80901..1a7e6a94 100644 --- a/back/src/whombat/api/evaluation_sets.py +++ b/back/src/whombat/api/evaluation_sets.py @@ -1,4 +1,5 @@ """API functions for interacting with evaluation sets.""" + import uuid from pathlib import Path from typing import Sequence diff --git a/back/src/whombat/api/io/aoef/clips.py b/back/src/whombat/api/io/aoef/clips.py index 8b3e296a..d2cf05ef 100644 --- a/back/src/whombat/api/io/aoef/clips.py +++ b/back/src/whombat/api/io/aoef/clips.py @@ -17,10 +17,12 @@ async def get_clips( session: AsyncSession, - obj: AnnotationSetObject - | EvaluationObject - | PredictionSetObject - | AnnotationProjectObject, + obj: ( + AnnotationSetObject + | EvaluationObject + | PredictionSetObject + | AnnotationProjectObject + ), recordings: dict[UUID, int], feature_names: dict[str, int], should_import: bool = True, diff --git a/back/src/whombat/api/io/aoef/features.py b/back/src/whombat/api/io/aoef/features.py index e493ad7f..a163725f 100644 --- a/back/src/whombat/api/io/aoef/features.py +++ b/back/src/whombat/api/io/aoef/features.py @@ -15,10 +15,12 @@ async def get_feature_names( session: AsyncSession, - obj: AnnotationSetObject - | EvaluationObject - | PredictionSetObject - | RecordingSetObject, + obj: ( + AnnotationSetObject + | EvaluationObject + | PredictionSetObject + | RecordingSetObject + ), ) -> dict[str, int]: names: set[str] = set(feat.value for feat in GeometricFeature) diff --git a/back/src/whombat/api/model_runs.py b/back/src/whombat/api/model_runs.py index 34f84ec2..147c225d 100644 --- a/back/src/whombat/api/model_runs.py +++ b/back/src/whombat/api/model_runs.py @@ -1,4 +1,5 @@ """API functions to interact with model runs.""" + from pathlib import Path from typing import Sequence from uuid import UUID diff --git a/back/src/whombat/api/recordings.py b/back/src/whombat/api/recordings.py index 2cf2d938..5aeb0b92 100644 --- a/back/src/whombat/api/recordings.py +++ b/back/src/whombat/api/recordings.py @@ -1,4 +1,5 @@ """API functions for interacting with recordings.""" + import datetime import logging from functools import partial diff --git a/back/src/whombat/api/sessions.py b/back/src/whombat/api/sessions.py index fbf71a9d..f24f2867 100644 --- a/back/src/whombat/api/sessions.py +++ b/back/src/whombat/api/sessions.py @@ -1,4 +1,5 @@ """Python API to manage database sessions.""" + from contextlib import asynccontextmanager from typing import AsyncGenerator diff --git a/back/src/whombat/api/sound_event_annotations.py b/back/src/whombat/api/sound_event_annotations.py index e098ca2d..c02c4a5f 100644 --- a/back/src/whombat/api/sound_event_annotations.py +++ b/back/src/whombat/api/sound_event_annotations.py @@ -269,9 +269,11 @@ async def to_soundevent( return data.SoundEventAnnotation( uuid=annotation.uuid, created_on=annotation.created_on, - created_by=users.to_soundevent(annotation.created_by) - if annotation.created_by - else None, + created_by=( + users.to_soundevent(annotation.created_by) + if annotation.created_by + else None + ), sound_event=await sound_events.to_soundevent( session, annotation.sound_event, diff --git a/back/src/whombat/api/sound_event_evaluations.py b/back/src/whombat/api/sound_event_evaluations.py index b60c51bc..4f4e0550 100644 --- a/back/src/whombat/api/sound_event_evaluations.py +++ b/back/src/whombat/api/sound_event_evaluations.py @@ -1,4 +1,5 @@ """API functions to interact with sound event evaluations.""" + from pathlib import Path from uuid import UUID diff --git a/back/src/whombat/api/sound_events.py b/back/src/whombat/api/sound_events.py index 5d9ace61..0ddb4b42 100644 --- a/back/src/whombat/api/sound_events.py +++ b/back/src/whombat/api/sound_events.py @@ -345,9 +345,11 @@ async def update_geometric_features( sound_event = sound_event.model_copy( update=dict( features=[ - f - if f.name not in feature_mapping - else feature_mapping[f.name] + ( + f + if f.name not in feature_mapping + else feature_mapping[f.name] + ) for f in sound_event.features ] ) diff --git a/back/src/whombat/api/spectrograms.py b/back/src/whombat/api/spectrograms.py index f7238530..3466f08c 100644 --- a/back/src/whombat/api/spectrograms.py +++ b/back/src/whombat/api/spectrograms.py @@ -1,4 +1,5 @@ """API functions to generate spectrograms.""" + from pathlib import Path import numpy as np diff --git a/back/src/whombat/api/tags.py b/back/src/whombat/api/tags.py index b266db7e..927a2ce4 100644 --- a/back/src/whombat/api/tags.py +++ b/back/src/whombat/api/tags.py @@ -1,4 +1,5 @@ """API functions to interact with tags.""" + from typing import Any, Sequence from soundevent import data diff --git a/back/src/whombat/api/users.py b/back/src/whombat/api/users.py index e788ee1e..68973fbe 100644 --- a/back/src/whombat/api/users.py +++ b/back/src/whombat/api/users.py @@ -1,4 +1,5 @@ """Whombat Python API to interact with user objects in the database.""" + import secrets from uuid import UUID diff --git a/back/src/whombat/app.py b/back/src/whombat/app.py index 69c7fb39..d900a2df 100644 --- a/back/src/whombat/app.py +++ b/back/src/whombat/app.py @@ -2,6 +2,7 @@ It contains the FastAPI instance and the root endpoint. """ + from whombat.system import create_app, get_settings settings = get_settings() diff --git a/back/src/whombat/cache.py b/back/src/whombat/cache.py index 4fa9c940..0538a255 100644 --- a/back/src/whombat/cache.py +++ b/back/src/whombat/cache.py @@ -1,4 +1,5 @@ """Cache functions.""" + from contextlib import AbstractContextManager from functools import wraps from typing import ( diff --git a/back/src/whombat/core/files.py b/back/src/whombat/core/files.py index 0cf02990..2fc72ae4 100644 --- a/back/src/whombat/core/files.py +++ b/back/src/whombat/core/files.py @@ -1,4 +1,5 @@ """File handling functions.""" + import logging from dataclasses import dataclass from pathlib import Path diff --git a/back/src/whombat/exceptions.py b/back/src/whombat/exceptions.py index e530af6a..380bd252 100644 --- a/back/src/whombat/exceptions.py +++ b/back/src/whombat/exceptions.py @@ -1,6 +1,5 @@ """Custom exceptions for Whombat.""" - __all__ = [ "NotFoundError", "DuplicateObjectError", diff --git a/back/src/whombat/filters/annotation_tasks.py b/back/src/whombat/filters/annotation_tasks.py index 4c4f1e6a..1c5b1b5e 100644 --- a/back/src/whombat/filters/annotation_tasks.py +++ b/back/src/whombat/filters/annotation_tasks.py @@ -1,4 +1,5 @@ """Filters for Annotation Tasks.""" + from uuid import UUID from soundevent import data diff --git a/back/src/whombat/filters/clip_predictions.py b/back/src/whombat/filters/clip_predictions.py index 94d234de..8935a66c 100644 --- a/back/src/whombat/filters/clip_predictions.py +++ b/back/src/whombat/filters/clip_predictions.py @@ -1,4 +1,5 @@ """Filters for Clip Predictions.""" + from uuid import UUID from sqlalchemy import Select, and_, select diff --git a/back/src/whombat/filters/evaluations.py b/back/src/whombat/filters/evaluations.py index 9d74e628..96e5928d 100644 --- a/back/src/whombat/filters/evaluations.py +++ b/back/src/whombat/filters/evaluations.py @@ -1,4 +1,5 @@ """Filters for Evaluations.""" + from uuid import UUID from sqlalchemy import Select, or_, select diff --git a/back/src/whombat/filters/feature_names.py b/back/src/whombat/filters/feature_names.py index 091ee110..72b1bdb5 100644 --- a/back/src/whombat/filters/feature_names.py +++ b/back/src/whombat/filters/feature_names.py @@ -1,4 +1,5 @@ """Filters for featuren names.""" + from uuid import UUID from sqlalchemy import Select diff --git a/back/src/whombat/filters/notes.py b/back/src/whombat/filters/notes.py index 1453a89c..f3b19261 100644 --- a/back/src/whombat/filters/notes.py +++ b/back/src/whombat/filters/notes.py @@ -1,4 +1,5 @@ """Filters for Notes.""" + from uuid import UUID from sqlalchemy import Select diff --git a/back/src/whombat/filters/sound_event_annotations.py b/back/src/whombat/filters/sound_event_annotations.py index cb1b41d5..e325c5f6 100644 --- a/back/src/whombat/filters/sound_event_annotations.py +++ b/back/src/whombat/filters/sound_event_annotations.py @@ -1,4 +1,5 @@ """Filters for Annotations.""" + from uuid import UUID from sqlalchemy import Select, select, tuple_ diff --git a/back/src/whombat/filters/sound_event_predictions.py b/back/src/whombat/filters/sound_event_predictions.py index 9c94a4c1..27607f0e 100644 --- a/back/src/whombat/filters/sound_event_predictions.py +++ b/back/src/whombat/filters/sound_event_predictions.py @@ -1,4 +1,5 @@ """Filters for Predictions.""" + from uuid import UUID from sqlalchemy import Select, and_ diff --git a/back/src/whombat/filters/sound_events.py b/back/src/whombat/filters/sound_events.py index 53256963..a1319947 100644 --- a/back/src/whombat/filters/sound_events.py +++ b/back/src/whombat/filters/sound_events.py @@ -1,4 +1,5 @@ """Filters for Sound Events.""" + from uuid import UUID from sqlalchemy import Select diff --git a/back/src/whombat/filters/tags.py b/back/src/whombat/filters/tags.py index a9236aba..5c2cd20d 100644 --- a/back/src/whombat/filters/tags.py +++ b/back/src/whombat/filters/tags.py @@ -1,4 +1,5 @@ """Filters for tags.""" + from uuid import UUID from sqlalchemy import Select diff --git a/back/src/whombat/migrations/versions/03a1d239e06e_first_migration.py b/back/src/whombat/migrations/versions/03a1d239e06e_first_migration.py index b0f73542..fc3cf464 100644 --- a/back/src/whombat/migrations/versions/03a1d239e06e_first_migration.py +++ b/back/src/whombat/migrations/versions/03a1d239e06e_first_migration.py @@ -5,6 +5,7 @@ Create Date: 2024-01-10 20:08:09.823754 """ + from typing import Sequence, Union import fastapi_users_db_sqlalchemy.generics diff --git a/back/src/whombat/models/__init__.py b/back/src/whombat/models/__init__.py index a083f3bf..9c99b76d 100644 --- a/back/src/whombat/models/__init__.py +++ b/back/src/whombat/models/__init__.py @@ -6,6 +6,7 @@ models into other modules without having to import the entire database module. """ + from whombat.models.annotation_project import ( AnnotationProject, AnnotationProjectTag, diff --git a/back/src/whombat/models/annotation_project.py b/back/src/whombat/models/annotation_project.py index e951c9a4..a921ef04 100644 --- a/back/src/whombat/models/annotation_project.py +++ b/back/src/whombat/models/annotation_project.py @@ -105,12 +105,12 @@ class AnnotationProject(Base): ) # Secondary relationships - annotation_project_tags: orm.Mapped[ - list["AnnotationProjectTag"] - ] = orm.relationship( - "AnnotationProjectTag", - default_factory=list, - cascade="all, delete-orphan", + annotation_project_tags: orm.Mapped[list["AnnotationProjectTag"]] = ( + orm.relationship( + "AnnotationProjectTag", + default_factory=list, + cascade="all, delete-orphan", + ) ) diff --git a/back/src/whombat/models/annotation_task.py b/back/src/whombat/models/annotation_task.py index 9235afb6..e829728d 100644 --- a/back/src/whombat/models/annotation_task.py +++ b/back/src/whombat/models/annotation_task.py @@ -107,15 +107,15 @@ class AnnotationTask(Base): init=False, single_parent=True, ) - status_badges: orm.Mapped[ - list["AnnotationStatusBadge"] - ] = orm.relationship( - back_populates="annotation_task", - cascade="all", - lazy="joined", - init=False, - repr=False, - default_factory=list, + status_badges: orm.Mapped[list["AnnotationStatusBadge"]] = ( + orm.relationship( + back_populates="annotation_task", + cascade="all", + lazy="joined", + init=False, + repr=False, + default_factory=list, + ) ) diff --git a/back/src/whombat/models/clip_annotation.py b/back/src/whombat/models/clip_annotation.py index 01253302..5f3f7372 100644 --- a/back/src/whombat/models/clip_annotation.py +++ b/back/src/whombat/models/clip_annotation.py @@ -96,21 +96,21 @@ class ClipAnnotation(Base): ) # Secondary relations - clip_annotation_notes: orm.Mapped[ - list["ClipAnnotationNote"] - ] = orm.relationship( - default_factory=list, - cascade="all, delete-orphan", - repr=False, - init=False, - ) - clip_annotation_tags: orm.Mapped[ - list["ClipAnnotationTag"] - ] = orm.relationship( - default_factory=list, - cascade="all, delete-orphan", - repr=False, - init=False, + clip_annotation_notes: orm.Mapped[list["ClipAnnotationNote"]] = ( + orm.relationship( + default_factory=list, + cascade="all, delete-orphan", + repr=False, + init=False, + ) + ) + clip_annotation_tags: orm.Mapped[list["ClipAnnotationTag"]] = ( + orm.relationship( + default_factory=list, + cascade="all, delete-orphan", + repr=False, + init=False, + ) ) # Backrefs @@ -127,14 +127,14 @@ class ClipAnnotation(Base): default_factory=list, viewonly=True, ) - evaluation_set_annotations: orm.Mapped[ - list["EvaluationSetAnnotation"] - ] = orm.relationship( - back_populates="clip_annotation", - init=False, - repr=False, - default_factory=list, - cascade="all, delete-orphan", + evaluation_set_annotations: orm.Mapped[list["EvaluationSetAnnotation"]] = ( + orm.relationship( + back_populates="clip_annotation", + init=False, + repr=False, + default_factory=list, + cascade="all, delete-orphan", + ) ) diff --git a/back/src/whombat/models/clip_evaluation.py b/back/src/whombat/models/clip_evaluation.py index 4b8b26e6..96b6499f 100644 --- a/back/src/whombat/models/clip_evaluation.py +++ b/back/src/whombat/models/clip_evaluation.py @@ -97,15 +97,15 @@ class ClipEvaluation(Base): init=False, lazy="selectin", ) - sound_event_evaluations: orm.Mapped[ - list[SoundEventEvaluation] - ] = orm.relationship( - back_populates="clip_evaluation", - cascade="all", - lazy="joined", - init=False, - repr=False, - default_factory=list, + sound_event_evaluations: orm.Mapped[list[SoundEventEvaluation]] = ( + orm.relationship( + back_populates="clip_evaluation", + cascade="all", + lazy="joined", + init=False, + repr=False, + default_factory=list, + ) ) metrics: orm.Mapped[list["ClipEvaluationMetric"]] = orm.relationship( back_populates="clip_evaluation", diff --git a/back/src/whombat/models/dataset.py b/back/src/whombat/models/dataset.py index fc1c40eb..2af8bf93 100644 --- a/back/src/whombat/models/dataset.py +++ b/back/src/whombat/models/dataset.py @@ -94,15 +94,15 @@ class Dataset(Base): ) # Secondary relations - dataset_recordings: orm.Mapped[ - list["DatasetRecording"] - ] = orm.relationship( - "DatasetRecording", - init=False, - repr=False, - back_populates="dataset", - cascade="all, delete-orphan", - default_factory=list, + dataset_recordings: orm.Mapped[list["DatasetRecording"]] = ( + orm.relationship( + "DatasetRecording", + init=False, + repr=False, + back_populates="dataset", + cascade="all, delete-orphan", + default_factory=list, + ) ) diff --git a/back/src/whombat/models/evaluation_set.py b/back/src/whombat/models/evaluation_set.py index c9238a7f..8471634b 100644 --- a/back/src/whombat/models/evaluation_set.py +++ b/back/src/whombat/models/evaluation_set.py @@ -1,4 +1,5 @@ """Evaluation set model.""" + from uuid import UUID, uuid4 import sqlalchemy.orm as orm @@ -165,34 +166,34 @@ class EvaluationSet(Base): ) # Secondary relationships - evaluation_set_annotations: orm.Mapped[ - list["EvaluationSetAnnotation"] - ] = orm.relationship( - back_populates="evaluation_set", - default_factory=list, - cascade="all, delete-orphan", + evaluation_set_annotations: orm.Mapped[list["EvaluationSetAnnotation"]] = ( + orm.relationship( + back_populates="evaluation_set", + default_factory=list, + cascade="all, delete-orphan", + ) ) - evaluation_set_tags: orm.Mapped[ - list["EvaluationSetTag"] - ] = orm.relationship( - lazy="joined", - default_factory=list, - cascade="all, delete-orphan", + evaluation_set_tags: orm.Mapped[list["EvaluationSetTag"]] = ( + orm.relationship( + lazy="joined", + default_factory=list, + cascade="all, delete-orphan", + ) ) """Set of tags to focus on for this evaluation set.""" - evaluation_set_model_runs: orm.Mapped[ - list["EvaluationSetModelRun"] - ] = orm.relationship( - back_populates="evaluation_set", - default_factory=list, - cascade="all, delete-orphan", + evaluation_set_model_runs: orm.Mapped[list["EvaluationSetModelRun"]] = ( + orm.relationship( + back_populates="evaluation_set", + default_factory=list, + cascade="all, delete-orphan", + ) ) - evaluation_set_user_runs: orm.Mapped[ - list["EvaluationSetUserRun"] - ] = orm.relationship( - back_populates="evaluation_set", - default_factory=list, - cascade="all, delete-orphan", + evaluation_set_user_runs: orm.Mapped[list["EvaluationSetUserRun"]] = ( + orm.relationship( + back_populates="evaluation_set", + default_factory=list, + cascade="all, delete-orphan", + ) ) diff --git a/back/src/whombat/models/model_run.py b/back/src/whombat/models/model_run.py index 38ede723..d72ef799 100644 --- a/back/src/whombat/models/model_run.py +++ b/back/src/whombat/models/model_run.py @@ -86,19 +86,19 @@ class ModelRun(Base): ) # Secondary relations - model_run_predictions: orm.Mapped[ - list["ModelRunPrediction"] - ] = orm.relationship( - init=False, - repr=False, - cascade="all, delete-orphan", + model_run_predictions: orm.Mapped[list["ModelRunPrediction"]] = ( + orm.relationship( + init=False, + repr=False, + cascade="all, delete-orphan", + ) ) - model_run_evaluations: orm.Mapped[ - list["ModelRunEvaluation"] - ] = orm.relationship( - init=False, - repr=False, - cascade="all, delete-orphan", + model_run_evaluations: orm.Mapped[list["ModelRunEvaluation"]] = ( + orm.relationship( + init=False, + repr=False, + cascade="all, delete-orphan", + ) ) # Backrefs @@ -110,14 +110,14 @@ class ModelRun(Base): default_factory=list, viewonly=True, ) - evaluation_set_model_runs: orm.Mapped[ - list["EvaluationSetModelRun"] - ] = orm.relationship( - back_populates="model_run", - cascade="all, delete-orphan", - init=False, - repr=False, - default_factory=list, + evaluation_set_model_runs: orm.Mapped[list["EvaluationSetModelRun"]] = ( + orm.relationship( + back_populates="model_run", + cascade="all, delete-orphan", + init=False, + repr=False, + default_factory=list, + ) ) diff --git a/back/src/whombat/models/note.py b/back/src/whombat/models/note.py index 1d63cdbe..4cdc94dd 100644 --- a/back/src/whombat/models/note.py +++ b/back/src/whombat/models/note.py @@ -119,15 +119,15 @@ class Note(Base): cascade="all, delete-orphan", ) - sound_event_annotation: orm.Mapped[ - Optional["SoundEventAnnotation"] - ] = orm.relationship( - "SoundEventAnnotation", - secondary="sound_event_annotation_note", - init=False, - repr=False, - viewonly=True, - back_populates="notes", + sound_event_annotation: orm.Mapped[Optional["SoundEventAnnotation"]] = ( + orm.relationship( + "SoundEventAnnotation", + secondary="sound_event_annotation_note", + init=False, + repr=False, + viewonly=True, + back_populates="notes", + ) ) sound_event_annotation_note: orm.Mapped[ @@ -150,13 +150,13 @@ class Note(Base): back_populates="notes", ) - clip_annotation_note: orm.Mapped[ - Optional["ClipAnnotationNote"] - ] = orm.relationship( - "ClipAnnotationNote", - init=False, - repr=False, - back_populates="note", - single_parent=True, - cascade="all, delete-orphan", + clip_annotation_note: orm.Mapped[Optional["ClipAnnotationNote"]] = ( + orm.relationship( + "ClipAnnotationNote", + init=False, + repr=False, + back_populates="note", + single_parent=True, + cascade="all, delete-orphan", + ) ) diff --git a/back/src/whombat/models/recording.py b/back/src/whombat/models/recording.py index 59fab2c4..dc927b77 100644 --- a/back/src/whombat/models/recording.py +++ b/back/src/whombat/models/recording.py @@ -204,14 +204,14 @@ class Recording(Base): cascade="all, delete-orphan", repr=False, ) - recording_datasets: orm.Mapped[ - list["DatasetRecording"] - ] = orm.relationship( - init=False, - repr=False, - cascade="all, delete-orphan", - back_populates="recording", - default_factory=list, + recording_datasets: orm.Mapped[list["DatasetRecording"]] = ( + orm.relationship( + init=False, + repr=False, + cascade="all, delete-orphan", + back_populates="recording", + default_factory=list, + ) ) diff --git a/back/src/whombat/models/sound_event.py b/back/src/whombat/models/sound_event.py index 1ce2587a..5840d2be 100644 --- a/back/src/whombat/models/sound_event.py +++ b/back/src/whombat/models/sound_event.py @@ -113,19 +113,19 @@ class SoundEvent(Base): ) # Backrefs - sound_event_annotation: orm.Mapped[ - Optional["SoundEventAnnotation"] - ] = orm.relationship( - back_populates="sound_event", - init=False, - repr=False, + sound_event_annotation: orm.Mapped[Optional["SoundEventAnnotation"]] = ( + orm.relationship( + back_populates="sound_event", + init=False, + repr=False, + ) ) - sound_event_prediction: orm.Mapped[ - Optional["SoundEventPrediction"] - ] = orm.relationship( - back_populates="sound_event", - init=False, - repr=False, + sound_event_prediction: orm.Mapped[Optional["SoundEventPrediction"]] = ( + orm.relationship( + back_populates="sound_event", + init=False, + repr=False, + ) ) diff --git a/back/src/whombat/models/sound_event_annotation.py b/back/src/whombat/models/sound_event_annotation.py index afcd5b6a..c0a4cc93 100644 --- a/back/src/whombat/models/sound_event_annotation.py +++ b/back/src/whombat/models/sound_event_annotation.py @@ -1,4 +1,5 @@ """Annotation model.""" + from typing import TYPE_CHECKING, Optional from uuid import UUID, uuid4 @@ -166,12 +167,12 @@ class SoundEventAnnotationNote(Base): primary_key=True, nullable=False, ) - sound_event_annotation: orm.Mapped[ - SoundEventAnnotation - ] = orm.relationship( - back_populates="sound_event_annotation_notes", - init=False, - repr=False, + sound_event_annotation: orm.Mapped[SoundEventAnnotation] = ( + orm.relationship( + back_populates="sound_event_annotation_notes", + init=False, + repr=False, + ) ) note: orm.Mapped[Note] = orm.relationship( back_populates="sound_event_annotation_note", @@ -222,12 +223,12 @@ class SoundEventAnnotationTag(Base): ) # Relationships - sound_event_annotation: orm.Mapped[ - SoundEventAnnotation - ] = orm.relationship( - back_populates="sound_event_annotation_tags", - init=False, - repr=False, + sound_event_annotation: orm.Mapped[SoundEventAnnotation] = ( + orm.relationship( + back_populates="sound_event_annotation_tags", + init=False, + repr=False, + ) ) tag: orm.Mapped[Tag] = orm.relationship( back_populates="sound_event_annotation_tags", diff --git a/back/src/whombat/models/sound_event_evaluation.py b/back/src/whombat/models/sound_event_evaluation.py index 943fcfb1..f724df29 100644 --- a/back/src/whombat/models/sound_event_evaluation.py +++ b/back/src/whombat/models/sound_event_evaluation.py @@ -167,10 +167,10 @@ class SoundEventEvaluationMetric(Base): repr=False, lazy="joined", ) - sound_event_evaluation: orm.Mapped[ - SoundEventEvaluation - ] = orm.relationship( - back_populates="metrics", - init=False, - repr=False, + sound_event_evaluation: orm.Mapped[SoundEventEvaluation] = ( + orm.relationship( + back_populates="metrics", + init=False, + repr=False, + ) ) diff --git a/back/src/whombat/models/sound_event_prediction.py b/back/src/whombat/models/sound_event_prediction.py index a8653312..b792f8e3 100644 --- a/back/src/whombat/models/sound_event_prediction.py +++ b/back/src/whombat/models/sound_event_prediction.py @@ -9,6 +9,7 @@ score reflecting the confidence of the model that the tag is relevant to the event. """ + from typing import TYPE_CHECKING from uuid import UUID, uuid4 diff --git a/back/src/whombat/models/tag.py b/back/src/whombat/models/tag.py index 8e1b5f6a..d67dafaf 100644 --- a/back/src/whombat/models/tag.py +++ b/back/src/whombat/models/tag.py @@ -36,6 +36,7 @@ these objects, users can more easily organize, filter, and analyze audio data, making it simpler to extract meaningful insights and information. """ + from typing import TYPE_CHECKING import sqlalchemy.orm as orm @@ -115,15 +116,15 @@ class Tag(Base): repr=False, default_factory=list, ) - sound_event_annotations: orm.Mapped[ - list["SoundEventAnnotation"] - ] = orm.relationship( - back_populates="tags", - secondary="sound_event_annotation_tag", - init=False, - repr=False, - viewonly=True, - default_factory=list, + sound_event_annotations: orm.Mapped[list["SoundEventAnnotation"]] = ( + orm.relationship( + back_populates="tags", + secondary="sound_event_annotation_tag", + init=False, + repr=False, + viewonly=True, + default_factory=list, + ) ) sound_event_annotation_tags: orm.Mapped[ list["SoundEventAnnotationTag"] @@ -141,39 +142,39 @@ class Tag(Base): viewonly=True, default_factory=list, ) - clip_annotation_tags: orm.Mapped[ - list["ClipAnnotationTag"] - ] = orm.relationship( - back_populates="tag", - init=False, - repr=False, - default_factory=list, + clip_annotation_tags: orm.Mapped[list["ClipAnnotationTag"]] = ( + orm.relationship( + back_populates="tag", + init=False, + repr=False, + default_factory=list, + ) ) - evaluation_set_tags: orm.Mapped[ - list["EvaluationSetTag"] - ] = orm.relationship( - back_populates="tag", - init=False, - repr=False, - default_factory=list, + evaluation_set_tags: orm.Mapped[list["EvaluationSetTag"]] = ( + orm.relationship( + back_populates="tag", + init=False, + repr=False, + default_factory=list, + ) ) - annotation_projects: orm.Mapped[ - list["AnnotationProject"] - ] = orm.relationship( - back_populates="tags", - secondary="annotation_project_tag", - init=False, - repr=False, - viewonly=True, - default_factory=list, + annotation_projects: orm.Mapped[list["AnnotationProject"]] = ( + orm.relationship( + back_populates="tags", + secondary="annotation_project_tag", + init=False, + repr=False, + viewonly=True, + default_factory=list, + ) ) - annotation_project_tags: orm.Mapped[ - list["AnnotationProjectTag"] - ] = orm.relationship( - back_populates="tag", - init=False, - repr=False, - default_factory=list, + annotation_project_tags: orm.Mapped[list["AnnotationProjectTag"]] = ( + orm.relationship( + back_populates="tag", + init=False, + repr=False, + default_factory=list, + ) ) sound_event_prediction_tags: orm.Mapped[ list["SoundEventPredictionTag"] @@ -183,11 +184,11 @@ class Tag(Base): repr=False, default_factory=list, ) - clip_prediction_tags: orm.Mapped[ - list["ClipPredictionTag"] - ] = orm.relationship( - back_populates="tag", - init=False, - repr=False, - default_factory=list, + clip_prediction_tags: orm.Mapped[list["ClipPredictionTag"]] = ( + orm.relationship( + back_populates="tag", + init=False, + repr=False, + default_factory=list, + ) ) diff --git a/back/src/whombat/models/token.py b/back/src/whombat/models/token.py index a5861d33..cb479050 100644 --- a/back/src/whombat/models/token.py +++ b/back/src/whombat/models/token.py @@ -1,4 +1,5 @@ """Module for defining the AccessToken Model.""" + import datetime from uuid import UUID diff --git a/back/src/whombat/models/user.py b/back/src/whombat/models/user.py index 93f21c59..552fa9a6 100644 --- a/back/src/whombat/models/user.py +++ b/back/src/whombat/models/user.py @@ -12,6 +12,7 @@ Additional information can be added, such as a full name, email, and affiliation. This information is not required. """ + from typing import TYPE_CHECKING, Optional from uuid import UUID, uuid4 diff --git a/back/src/whombat/models/user_run.py b/back/src/whombat/models/user_run.py index d5582ee6..3e63ae92 100644 --- a/back/src/whombat/models/user_run.py +++ b/back/src/whombat/models/user_run.py @@ -85,19 +85,19 @@ class UserRun(Base): ) # Secondary relations - user_run_predictions: orm.Mapped[ - list["UserRunPrediction"] - ] = orm.relationship( - init=False, - repr=False, - cascade="all, delete-orphan", + user_run_predictions: orm.Mapped[list["UserRunPrediction"]] = ( + orm.relationship( + init=False, + repr=False, + cascade="all, delete-orphan", + ) ) - user_run_evaluations: orm.Mapped[ - list["UserRunEvaluation"] - ] = orm.relationship( - init=False, - repr=False, - cascade="all, delete-orphan", + user_run_evaluations: orm.Mapped[list["UserRunEvaluation"]] = ( + orm.relationship( + init=False, + repr=False, + cascade="all, delete-orphan", + ) ) # Backrefs @@ -109,13 +109,13 @@ class UserRun(Base): default_factory=list, viewonly=True, ) - evaluation_set_user_runs: orm.Mapped[ - list["EvaluationSetUserRun"] - ] = orm.relationship( - back_populates="user_run", - init=False, - repr=False, - default_factory=list, + evaluation_set_user_runs: orm.Mapped[list["EvaluationSetUserRun"]] = ( + orm.relationship( + back_populates="user_run", + init=False, + repr=False, + default_factory=list, + ) ) diff --git a/back/src/whombat/routes/__init__.py b/back/src/whombat/routes/__init__.py index 17b010bd..6742d366 100644 --- a/back/src/whombat/routes/__init__.py +++ b/back/src/whombat/routes/__init__.py @@ -1,11 +1,12 @@ """Whombat REST API routes.""" + from fastapi import APIRouter from whombat.routes.annotation_projects import annotation_projects_router -from whombat.routes.annotation_tasks import annotation_tasks_router +from whombat.routes.annotation_tasks import get_annotation_tasks_router from whombat.routes.audio import audio_router from whombat.routes.auth import get_auth_router -from whombat.routes.clip_annotations import clip_annotations_router +from whombat.routes.clip_annotations import get_clip_annotations_router from whombat.routes.clip_evaluations import clip_evaluations_router from whombat.routes.clip_predictions import clip_predictions_router from whombat.routes.clips import clips_router @@ -16,9 +17,9 @@ from whombat.routes.model_runs import model_runs_router from whombat.routes.notes import notes_router from whombat.routes.plugins import plugin_router -from whombat.routes.recordings import recording_router +from whombat.routes.recordings import get_recording_router from whombat.routes.sound_event_annotations import ( - sound_event_annotations_router, + get_sound_event_annotations_router, ) from whombat.routes.sound_event_evaluations import ( sound_event_evaluations_router, @@ -29,7 +30,7 @@ from whombat.routes.sound_events import sound_events_router from whombat.routes.spectrograms import spectrograms_router from whombat.routes.tags import tags_router -from whombat.routes.user_runs import user_runs_router +from whombat.routes.user_runs import get_user_runs_router from whombat.routes.users import get_users_router from whombat.system.settings import Settings @@ -73,6 +74,7 @@ def get_main_router(settings: Settings): ) # Audio Metadata + recording_router = get_recording_router(settings) main_router.include_router( recording_router, prefix="/recordings", @@ -109,16 +111,21 @@ def get_main_router(settings: Settings): ) # Annotation + sound_event_annotations_router = get_sound_event_annotations_router( + settings + ) main_router.include_router( sound_event_annotations_router, prefix="/sound_event_annotations", tags=["Sound Event Annotations"], ) + clip_annotations_router = get_clip_annotations_router(settings) main_router.include_router( clip_annotations_router, prefix="/clip_annotations", tags=["Clip Annotations"], ) + annotation_tasks_router = get_annotation_tasks_router(settings) main_router.include_router( annotation_tasks_router, prefix="/annotation_tasks", @@ -146,6 +153,7 @@ def get_main_router(settings: Settings): prefix="/model_runs", tags=["Model Runs"], ) + user_runs_router = get_user_runs_router(settings) main_router.include_router( user_runs_router, prefix="/user_runs", diff --git a/back/src/whombat/routes/annotation_projects.py b/back/src/whombat/routes/annotation_projects.py index 2ddd56fe..880180cd 100644 --- a/back/src/whombat/routes/annotation_projects.py +++ b/back/src/whombat/routes/annotation_projects.py @@ -1,4 +1,5 @@ """REST API routes for annotation projects.""" + import json from uuid import UUID diff --git a/back/src/whombat/routes/annotation_tasks.py b/back/src/whombat/routes/annotation_tasks.py index 19726984..7c78cadc 100644 --- a/back/src/whombat/routes/annotation_tasks.py +++ b/back/src/whombat/routes/annotation_tasks.py @@ -1,4 +1,5 @@ """REST API routes for annotation tasks.""" + from uuid import UUID from fastapi import APIRouter, Depends @@ -7,182 +8,183 @@ from whombat import api, schemas from whombat.filters.annotation_tasks import AnnotationTaskFilter from whombat.filters.clips import UUIDFilter as ClipUUIDFilter -from whombat.routes.dependencies import ActiveUser, Session +from whombat.routes.dependencies import Session, get_current_user_dependency +from whombat.routes.dependencies.settings import WhombatSettings from whombat.routes.types import Limit, Offset __all__ = [ - "annotation_tasks_router", + "get_annotation_tasks_router", ] -annotation_tasks_router = APIRouter() +def get_annotation_tasks_router(settings: WhombatSettings) -> APIRouter: + """Get the API router for annotation tasks.""" + active_user = get_current_user_dependency(settings) -@annotation_tasks_router.post( - "/", - response_model=list[schemas.AnnotationTask], -) -async def create_tasks( - session: Session, - annotation_project_uuid: UUID, - clip_uuids: list[UUID], -): - """Create multiple annotation tasks.""" - annotation_project = await api.annotation_projects.get( - session, - annotation_project_uuid, - ) - clips, _ = await api.clips.get_many( - session, - limit=-1, - filters=[ - ClipUUIDFilter( - isin=clip_uuids, - ), - ], - ) - # Create empty clip annotations - clip_annotations = await api.clip_annotations.create_many( - session, - data=[dict(clip_id=clip.id) for clip in clips], - ) - tasks = await api.annotation_tasks.create_many_without_duplicates( - session, - data=[ - dict( - annotation_project_id=annotation_project.id, - clip_annotation_id=clip_annotation.id, - clip_id=clip_annotation.clip.id, - ) - for clip_annotation in clip_annotations - ], - return_all=True, - ) - await session.commit() - return tasks - - -@annotation_tasks_router.get( - "/", - response_model=schemas.Page[schemas.AnnotationTask], -) -async def get_tasks( - session: Session, - limit: Limit = 10, - offset: Offset = 0, - filter: AnnotationTaskFilter = Depends(AnnotationTaskFilter), # type: ignore - sort_by: str = "-created_on", -): - """Get a page of annotation tasks.""" - tasks, total = await api.annotation_tasks.get_many( - session, - limit=limit, - offset=offset, - filters=[filter], - sort_by=sort_by, - ) - return schemas.Page( - items=tasks, - total=total, - limit=limit, - offset=offset, - ) - + annotation_tasks_router = APIRouter() -@annotation_tasks_router.delete( - "/detail/", - response_model=schemas.AnnotationTask, -) -async def delete_task( - session: Session, - annotation_task_uuid: UUID, -): - """Remove a clip from an annotation project.""" - annotation_task = await api.annotation_tasks.get( - session, - annotation_task_uuid, - ) - annotation_task = await api.annotation_tasks.delete( - session, - annotation_task, + @annotation_tasks_router.post( + "/", + response_model=list[schemas.AnnotationTask], ) - await session.commit() - return annotation_task - - -@annotation_tasks_router.get( - "/detail/", - response_model=schemas.AnnotationTask, -) -async def get_task( - session: Session, - annotation_task_uuid: UUID, -): - """Get an annotation task.""" - return await api.annotation_tasks.get(session, annotation_task_uuid) - - -@annotation_tasks_router.get( - "/detail/clip_annotation/", - response_model=schemas.ClipAnnotation, -) -async def get_task_annotations( - session: Session, - annotation_task_uuid: UUID, -) -> schemas.ClipAnnotation: - """Get an annotation task.""" - annotation_task = await api.annotation_tasks.get( - session, - annotation_task_uuid, + async def create_tasks( + session: Session, + annotation_project_uuid: UUID, + clip_uuids: list[UUID], + ): + """Create multiple annotation tasks.""" + annotation_project = await api.annotation_projects.get( + session, + annotation_project_uuid, + ) + clips, _ = await api.clips.get_many( + session, + limit=-1, + filters=[ + ClipUUIDFilter( + isin=clip_uuids, + ), + ], + ) + # Create empty clip annotations + clip_annotations = await api.clip_annotations.create_many( + session, + data=[dict(clip_id=clip.id) for clip in clips], + ) + tasks = await api.annotation_tasks.create_many_without_duplicates( + session, + data=[ + dict( + annotation_project_id=annotation_project.id, + clip_annotation_id=clip_annotation.id, + clip_id=clip_annotation.clip.id, + ) + for clip_annotation in clip_annotations + ], + return_all=True, + ) + await session.commit() + return tasks + + @annotation_tasks_router.get( + "/", + response_model=schemas.Page[schemas.AnnotationTask], ) - return await api.annotation_tasks.get_clip_annotation( - session, annotation_task + async def get_tasks( + session: Session, + limit: Limit = 10, + offset: Offset = 0, + filter: AnnotationTaskFilter = Depends(AnnotationTaskFilter), # type: ignore + sort_by: str = "-created_on", + ): + """Get a page of annotation tasks.""" + tasks, total = await api.annotation_tasks.get_many( + session, + limit=limit, + offset=offset, + filters=[filter], + sort_by=sort_by, + ) + return schemas.Page( + items=tasks, + total=total, + limit=limit, + offset=offset, + ) + + @annotation_tasks_router.delete( + "/detail/", + response_model=schemas.AnnotationTask, ) - - -@annotation_tasks_router.post( - "/detail/badges/", - response_model=schemas.AnnotationTask, -) -async def add_annotation_status_badge( - session: Session, - annotation_task_uuid: UUID, - state: AnnotationState, - user: ActiveUser, -): - """Add a badge to an annotation task.""" - annotation_task = await api.annotation_tasks.get( - session, - annotation_task_uuid, + async def delete_task( + session: Session, + annotation_task_uuid: UUID, + ): + """Remove a clip from an annotation project.""" + annotation_task = await api.annotation_tasks.get( + session, + annotation_task_uuid, + ) + annotation_task = await api.annotation_tasks.delete( + session, + annotation_task, + ) + await session.commit() + return annotation_task + + @annotation_tasks_router.get( + "/detail/", + response_model=schemas.AnnotationTask, ) - updated = await api.annotation_tasks.add_status_badge( - session, - annotation_task, - state, - user, + async def get_task( + session: Session, + annotation_task_uuid: UUID, + ): + """Get an annotation task.""" + return await api.annotation_tasks.get(session, annotation_task_uuid) + + @annotation_tasks_router.get( + "/detail/clip_annotation/", + response_model=schemas.ClipAnnotation, ) - await session.commit() - return updated - - -@annotation_tasks_router.delete( - "/detail/badges/", - response_model=schemas.AnnotationTask, -) -async def remove_annotation_status_badge( - session: Session, - annotation_task_uuid: UUID, - state: AnnotationState, -): - """Remove a badge from an annotation task.""" - annotation_task = await api.annotation_tasks.get( - session, - annotation_task_uuid, + async def get_task_annotations( + session: Session, + annotation_task_uuid: UUID, + ) -> schemas.ClipAnnotation: + """Get an annotation task.""" + annotation_task = await api.annotation_tasks.get( + session, + annotation_task_uuid, + ) + return await api.annotation_tasks.get_clip_annotation( + session, annotation_task + ) + + @annotation_tasks_router.post( + "/detail/badges/", + response_model=schemas.AnnotationTask, ) - updated = await api.annotation_tasks.remove_status_badge( - session, - annotation_task, - state, + async def add_annotation_status_badge( + session: Session, + annotation_task_uuid: UUID, + state: AnnotationState, + user: schemas.SimpleUser = Depends(active_user), + ): + """Add a badge to an annotation task.""" + annotation_task = await api.annotation_tasks.get( + session, + annotation_task_uuid, + ) + updated = await api.annotation_tasks.add_status_badge( + session, + annotation_task, + state, + user, + ) + await session.commit() + return updated + + @annotation_tasks_router.delete( + "/detail/badges/", + response_model=schemas.AnnotationTask, ) - await session.commit() - return updated + async def remove_annotation_status_badge( + session: Session, + annotation_task_uuid: UUID, + state: AnnotationState, + ): + """Remove a badge from an annotation task.""" + annotation_task = await api.annotation_tasks.get( + session, + annotation_task_uuid, + ) + updated = await api.annotation_tasks.remove_status_badge( + session, + annotation_task, + state, + ) + await session.commit() + return updated + + return annotation_tasks_router diff --git a/back/src/whombat/routes/audio.py b/back/src/whombat/routes/audio.py index 35a5f094..315cf0db 100644 --- a/back/src/whombat/routes/audio.py +++ b/back/src/whombat/routes/audio.py @@ -1,4 +1,5 @@ """REST API routes for audio.""" + from io import BytesIO from uuid import UUID diff --git a/back/src/whombat/routes/auth.py b/back/src/whombat/routes/auth.py index 01791b59..73400987 100644 --- a/back/src/whombat/routes/auth.py +++ b/back/src/whombat/routes/auth.py @@ -1,4 +1,5 @@ """Module containing the router for the Auth.""" + from fastapi import APIRouter from whombat.routes.dependencies.auth import get_auth_backend, get_users_api diff --git a/back/src/whombat/routes/clip_annotations.py b/back/src/whombat/routes/clip_annotations.py index 24c4a497..e310e6c1 100644 --- a/back/src/whombat/routes/clip_annotations.py +++ b/back/src/whombat/routes/clip_annotations.py @@ -1,207 +1,208 @@ """REST API routes for clip_annotations.""" + from uuid import UUID from fastapi import APIRouter, Depends from whombat import api, schemas from whombat.filters.clip_annotations import ClipAnnotationFilter -from whombat.routes.dependencies import ActiveUser, Session +from whombat.routes.dependencies import Session, get_current_user_dependency +from whombat.routes.dependencies.settings import WhombatSettings from whombat.routes.types import Limit, Offset __all__ = [ - "clip_annotations_router", + "get_clip_annotations_router", ] -clip_annotations_router = APIRouter() - +def get_clip_annotations_router(settings: WhombatSettings) -> APIRouter: + """Get the API router for clip_annotations.""" -@clip_annotations_router.post( - "/", - response_model=schemas.ClipAnnotation, -) -async def create_annotation( - session: Session, - clip_uuid: UUID, -): - """Create annotation.""" - clip = await api.clips.get(session, clip_uuid) - clip_annotation = await api.clip_annotations.create( - session, - clip=clip, - ) - await session.commit() - return clip_annotation - - -@clip_annotations_router.get( - "/", - response_model=schemas.Page[schemas.ClipAnnotation], -) -async def get_clip_annotations( - session: Session, - limit: Limit = 10, - offset: Offset = 0, - filter: ClipAnnotationFilter = Depends(ClipAnnotationFilter), # type: ignore - sort_by: str = "-created_on", -): - """Get a page of annotation clip_annotations.""" - ( - clip_annotations, - total, - ) = await api.clip_annotations.get_many( - session, - limit=limit, - offset=offset, - filters=[filter], - sort_by=sort_by, - ) - return schemas.Page( - items=clip_annotations, - total=total, - limit=limit, - offset=offset, - ) + active_user = get_current_user_dependency(settings) + clip_annotations_router = APIRouter() -@clip_annotations_router.get( - "/detail/", - response_model=schemas.ClipAnnotation, -) -async def get_annotation( - session: Session, - clip_annotation_uuid: UUID, -): - """Get an annotation annotation.""" - clip_annotation = await api.clip_annotations.get( - session, - clip_annotation_uuid, - ) - return clip_annotation - - -@clip_annotations_router.delete( - "/detail/", - response_model=schemas.ClipAnnotation, -) -async def delete_annotation( - session: Session, - clip_annotation_uuid: UUID, -): - """Remove a clip from an annotation project.""" - clip_annotation = await api.clip_annotations.get( - session, - clip_annotation_uuid, - ) - clip_annotaiton = await api.clip_annotations.delete( - session, - clip_annotation, - ) - await session.commit() - return clip_annotaiton - - -@clip_annotations_router.post( - "/detail/tags/", - response_model=schemas.ClipAnnotation, -) -async def add_annotation_tag( - session: Session, - clip_annotation_uuid: UUID, - key: str, - value: str, - user: ActiveUser, -): - """Add a tag to an annotation annotation.""" - clip_annotation = await api.clip_annotations.get( - session, - clip_annotation_uuid, - ) - tag = await api.tags.get(session, (key, value)) - clip_annotation = await api.clip_annotations.add_tag( - session, - clip_annotation, - tag, - user, + @clip_annotations_router.post( + "/", + response_model=schemas.ClipAnnotation, ) - await session.commit() - return clip_annotation - - -@clip_annotations_router.delete( - "/detail/tags/", - response_model=schemas.ClipAnnotation, -) -async def remove_annotation_tag( - session: Session, - clip_annotation_uuid: UUID, - key: str, - value: str, -): - """Remove a tag from an annotation annotation.""" - clip_annotation = await api.clip_annotations.get( - session, - clip_annotation_uuid, + async def create_annotation( + session: Session, + clip_uuid: UUID, + ): + """Create annotation.""" + clip = await api.clips.get(session, clip_uuid) + clip_annotation = await api.clip_annotations.create( + session, + clip=clip, + ) + await session.commit() + return clip_annotation + + @clip_annotations_router.get( + "/", + response_model=schemas.Page[schemas.ClipAnnotation], ) - tag = await api.tags.get(session, (key, value)) - clip_annotation = await api.clip_annotations.remove_tag( - session, - clip_annotation, - tag, + async def get_clip_annotations( + session: Session, + limit: Limit = 10, + offset: Offset = 0, + filter: ClipAnnotationFilter = Depends(ClipAnnotationFilter), # type: ignore + sort_by: str = "-created_on", + ): + """Get a page of annotation clip_annotations.""" + ( + clip_annotations, + total, + ) = await api.clip_annotations.get_many( + session, + limit=limit, + offset=offset, + filters=[filter], + sort_by=sort_by, + ) + return schemas.Page( + items=clip_annotations, + total=total, + limit=limit, + offset=offset, + ) + + @clip_annotations_router.get( + "/detail/", + response_model=schemas.ClipAnnotation, ) - await session.commit() - return clip_annotation - - -@clip_annotations_router.post( - "/detail/notes/", - response_model=schemas.ClipAnnotation, -) -async def create_annotation_note( - session: Session, - clip_annotation_uuid: UUID, - data: schemas.NoteCreate, - user: ActiveUser, -): - """Create a note for an annotation annotation.""" - clip_annotation = await api.clip_annotations.get( - session, - clip_annotation_uuid, + async def get_annotation( + session: Session, + clip_annotation_uuid: UUID, + ): + """Get an annotation annotation.""" + clip_annotation = await api.clip_annotations.get( + session, + clip_annotation_uuid, + ) + return clip_annotation + + @clip_annotations_router.delete( + "/detail/", + response_model=schemas.ClipAnnotation, ) - note = await api.notes.create( - session, - message=data.message, - is_issue=data.is_issue, - created_by=user, + async def delete_annotation( + session: Session, + clip_annotation_uuid: UUID, + ): + """Remove a clip from an annotation project.""" + clip_annotation = await api.clip_annotations.get( + session, + clip_annotation_uuid, + ) + clip_annotaiton = await api.clip_annotations.delete( + session, + clip_annotation, + ) + await session.commit() + return clip_annotaiton + + @clip_annotations_router.post( + "/detail/tags/", + response_model=schemas.ClipAnnotation, ) - clip_annotation = await api.clip_annotations.add_note( - session, - clip_annotation, - note, + async def add_annotation_tag( + session: Session, + clip_annotation_uuid: UUID, + key: str, + value: str, + user: schemas.SimpleUser = Depends(active_user), + ): + """Add a tag to an annotation annotation.""" + clip_annotation = await api.clip_annotations.get( + session, + clip_annotation_uuid, + ) + tag = await api.tags.get(session, (key, value)) + clip_annotation = await api.clip_annotations.add_tag( + session, + clip_annotation, + tag, + user, + ) + await session.commit() + return clip_annotation + + @clip_annotations_router.delete( + "/detail/tags/", + response_model=schemas.ClipAnnotation, ) - await session.commit() - return clip_annotation - - -@clip_annotations_router.delete( - "/detail/notes/", - response_model=schemas.ClipAnnotation, -) -async def delete_annotation_note( - session: Session, - clip_annotation_uuid: UUID, - note_uuid: UUID, -): - """Delete a note from an annotation annotation.""" - clip_annotation = await api.clip_annotations.get( - session, - clip_annotation_uuid, + async def remove_annotation_tag( + session: Session, + clip_annotation_uuid: UUID, + key: str, + value: str, + ): + """Remove a tag from an annotation annotation.""" + clip_annotation = await api.clip_annotations.get( + session, + clip_annotation_uuid, + ) + tag = await api.tags.get(session, (key, value)) + clip_annotation = await api.clip_annotations.remove_tag( + session, + clip_annotation, + tag, + ) + await session.commit() + return clip_annotation + + @clip_annotations_router.post( + "/detail/notes/", + response_model=schemas.ClipAnnotation, ) - note = await api.notes.get(session, note_uuid) - clip_annotation = await api.clip_annotations.remove_note( - session, - clip_annotation, - note, + async def create_annotation_note( + session: Session, + clip_annotation_uuid: UUID, + data: schemas.NoteCreate, + user: schemas.SimpleUser = Depends(active_user), + ): + """Create a note for an annotation annotation.""" + clip_annotation = await api.clip_annotations.get( + session, + clip_annotation_uuid, + ) + note = await api.notes.create( + session, + message=data.message, + is_issue=data.is_issue, + created_by=user, + ) + clip_annotation = await api.clip_annotations.add_note( + session, + clip_annotation, + note, + ) + await session.commit() + return clip_annotation + + @clip_annotations_router.delete( + "/detail/notes/", + response_model=schemas.ClipAnnotation, ) - await session.commit() - return clip_annotation + async def delete_annotation_note( + session: Session, + clip_annotation_uuid: UUID, + note_uuid: UUID, + ): + """Delete a note from an annotation annotation.""" + clip_annotation = await api.clip_annotations.get( + session, + clip_annotation_uuid, + ) + note = await api.notes.get(session, note_uuid) + clip_annotation = await api.clip_annotations.remove_note( + session, + clip_annotation, + note, + ) + await session.commit() + return clip_annotation + + return clip_annotations_router diff --git a/back/src/whombat/routes/clip_evaluations.py b/back/src/whombat/routes/clip_evaluations.py index cf11751b..331a7ba2 100644 --- a/back/src/whombat/routes/clip_evaluations.py +++ b/back/src/whombat/routes/clip_evaluations.py @@ -1,4 +1,5 @@ """REST API routes for Sound Event Predictions.""" + from uuid import UUID from fastapi import APIRouter, Depends diff --git a/back/src/whombat/routes/clip_predictions.py b/back/src/whombat/routes/clip_predictions.py index 3ab06d3a..84fd076c 100644 --- a/back/src/whombat/routes/clip_predictions.py +++ b/back/src/whombat/routes/clip_predictions.py @@ -1,4 +1,5 @@ """REST API routes for Clip Predictions.""" + from uuid import UUID from fastapi import APIRouter, Depends diff --git a/back/src/whombat/routes/clips.py b/back/src/whombat/routes/clips.py index e47164ac..03b9f33a 100644 --- a/back/src/whombat/routes/clips.py +++ b/back/src/whombat/routes/clips.py @@ -1,4 +1,5 @@ """REST API routes for clips.""" + from uuid import UUID, uuid4 from fastapi import APIRouter, Depends diff --git a/back/src/whombat/routes/datasets.py b/back/src/whombat/routes/datasets.py index a8f4542f..0a4c7b51 100644 --- a/back/src/whombat/routes/datasets.py +++ b/back/src/whombat/routes/datasets.py @@ -1,4 +1,5 @@ """REST API routes for datasets.""" + import datetime import json from io import StringIO diff --git a/back/src/whombat/routes/dependencies/__init__.py b/back/src/whombat/routes/dependencies/__init__.py index 46de5c12..428a8bb9 100644 --- a/back/src/whombat/routes/dependencies/__init__.py +++ b/back/src/whombat/routes/dependencies/__init__.py @@ -1,13 +1,14 @@ """Common FastAPI dependencies for whombat.""" -from whombat.routes.dependencies.auth import ActiveUser + +from whombat.routes.dependencies.auth import get_current_user_dependency from whombat.routes.dependencies.session import Session from whombat.routes.dependencies.settings import WhombatSettings from whombat.routes.dependencies.users import get_user_db, get_user_manager __all__ = [ - "ActiveUser", "Session", "WhombatSettings", "get_user_db", "get_user_manager", + "get_current_user_dependency", ] diff --git a/back/src/whombat/routes/dependencies/auth.py b/back/src/whombat/routes/dependencies/auth.py index f244d2ea..3b832d10 100644 --- a/back/src/whombat/routes/dependencies/auth.py +++ b/back/src/whombat/routes/dependencies/auth.py @@ -1,9 +1,11 @@ """Authentication dependencies.""" + from typing import Annotated from uuid import UUID from fastapi import Depends from fastapi_users import FastAPIUsers +from fastapi_users.authentication import AuthenticationBackend from whombat import models, schemas from whombat.routes.dependencies.session import Session @@ -12,11 +14,17 @@ from whombat.system import auth -async def auth_strategy( +def get_access_token_db( session: Session, +): + """Get the access token database.""" + return auth.get_access_token_db(session) + + +async def get_database_strategy( + token_db: auth.TokenDatabase = Depends(get_access_token_db), ): """Get the authentication strategy.""" - token_db = auth.get_access_token_db(session) return auth.get_database_strategy(token_db) @@ -24,9 +32,11 @@ def get_auth_backend( settings: WhombatSettings, ): """Get the authentication strategy.""" - return auth.get_auth_backend( - settings, - auth_strategy, + cookie_transport = auth.get_cookie_transport(settings) + return AuthenticationBackend( + name="database", + transport=cookie_transport, + get_strategy=get_database_strategy, ) @@ -34,18 +44,14 @@ def get_users_api(settings: WhombatSettings): """Get the users API.""" auth_backend = get_auth_backend(settings) return FastAPIUsers[models.User, UUID]( # type: ignore - get_user_manager, # type: ignore + get_user_manager, [auth_backend], ) -async def get_current_user( +def get_current_user_dependency( settings: WhombatSettings, -) -> schemas.SimpleUser: +): """Get the current user.""" fastapi_users = get_users_api(settings) - current_active_user = fastapi_users.current_user(active=True) - return schemas.SimpleUser.model_validate(current_active_user) - - -ActiveUser = Annotated[schemas.SimpleUser, Depends(get_current_user)] + return fastapi_users.current_user(active=True) diff --git a/back/src/whombat/routes/dependencies/session.py b/back/src/whombat/routes/dependencies/session.py index d2b8cecf..e97ebbd2 100644 --- a/back/src/whombat/routes/dependencies/session.py +++ b/back/src/whombat/routes/dependencies/session.py @@ -1,4 +1,5 @@ """Common database session dependencies.""" + from typing import Annotated, AsyncGenerator from fastapi import Depends diff --git a/back/src/whombat/routes/dependencies/settings.py b/back/src/whombat/routes/dependencies/settings.py index d92fb17e..080404ba 100644 --- a/back/src/whombat/routes/dependencies/settings.py +++ b/back/src/whombat/routes/dependencies/settings.py @@ -1,4 +1,5 @@ """Settings dependencies.""" + from typing import Annotated from fastapi import Depends diff --git a/back/src/whombat/routes/dependencies/users.py b/back/src/whombat/routes/dependencies/users.py index 54655195..5f3d09e0 100644 --- a/back/src/whombat/routes/dependencies/users.py +++ b/back/src/whombat/routes/dependencies/users.py @@ -1,4 +1,5 @@ """User dependencies.""" + from typing import AsyncGenerator from fastapi import Depends diff --git a/back/src/whombat/routes/evaluation_sets.py b/back/src/whombat/routes/evaluation_sets.py index dc877259..93c0d8e1 100644 --- a/back/src/whombat/routes/evaluation_sets.py +++ b/back/src/whombat/routes/evaluation_sets.py @@ -1,4 +1,5 @@ """REST API routes for evaluation sets.""" + import json from typing import Annotated from uuid import UUID diff --git a/back/src/whombat/routes/evaluations.py b/back/src/whombat/routes/evaluations.py index 0a98b2f1..6002214b 100644 --- a/back/src/whombat/routes/evaluations.py +++ b/back/src/whombat/routes/evaluations.py @@ -1,4 +1,5 @@ """REST API routes for Sound Event Predictions.""" + from uuid import UUID from fastapi import APIRouter, Depends diff --git a/back/src/whombat/routes/model_runs.py b/back/src/whombat/routes/model_runs.py index d613b403..5f23da12 100644 --- a/back/src/whombat/routes/model_runs.py +++ b/back/src/whombat/routes/model_runs.py @@ -1,4 +1,5 @@ """REST API routes for model runs.""" + import json from uuid import UUID diff --git a/back/src/whombat/routes/notes.py b/back/src/whombat/routes/notes.py index d624d285..7b7718ed 100644 --- a/back/src/whombat/routes/notes.py +++ b/back/src/whombat/routes/notes.py @@ -1,4 +1,5 @@ """REST API routes for notes.""" + from uuid import UUID from fastapi import APIRouter, Depends diff --git a/back/src/whombat/routes/plugins.py b/back/src/whombat/routes/plugins.py index e9ce021b..102ffd82 100644 --- a/back/src/whombat/routes/plugins.py +++ b/back/src/whombat/routes/plugins.py @@ -1,4 +1,5 @@ """REST API routes for plugin discover.""" + from fastapi import APIRouter from whombat import plugins, schemas diff --git a/back/src/whombat/routes/recordings.py b/back/src/whombat/routes/recordings.py index 62003b1d..28b10278 100644 --- a/back/src/whombat/routes/recordings.py +++ b/back/src/whombat/routes/recordings.py @@ -1,230 +1,239 @@ """REST API routes for recordings.""" + from uuid import UUID from fastapi import APIRouter, Depends from whombat import api, schemas from whombat.filters.recordings import RecordingFilter -from whombat.routes.dependencies import ActiveUser, Session +from whombat.routes.dependencies import Session, get_current_user_dependency +from whombat.routes.dependencies.settings import WhombatSettings from whombat.routes.types import Limit, Offset __all__ = [ - "recording_router", + "get_recording_router", ] -recording_router = APIRouter() - - -@recording_router.get( - "/", - response_model=schemas.Page[schemas.Recording], - response_model_exclude_none=True, -) -async def get_recordings( - session: Session, - limit: Limit = 10, - offset: Offset = 0, - sort_by: str = "-created_on", - filter: RecordingFilter = Depends(RecordingFilter), # type: ignore -): - """Get a page of datasets.""" - datasets, total = await api.recordings.get_many( - session, - limit=limit, - offset=offset, - filters=[filter], - sort_by=sort_by, - ) - return schemas.Page( - items=datasets, - total=total, - offset=offset, - limit=limit, - ) +def get_recording_router(settings: WhombatSettings) -> APIRouter: + """Get the API router for recordings.""" + + active_user = get_current_user_dependency(settings) -@recording_router.get( - "/detail/", - response_model=schemas.Recording, - response_model_exclude_none=True, -) -async def get_recording( - session: Session, - recording_uuid: UUID, -): - """Get a recording.""" - return await api.recordings.get(session, recording_uuid) - - -@recording_router.patch( - "/detail/", - response_model=schemas.Recording, - response_model_exclude_none=True, -) -async def update_recording( - session: Session, - recording_uuid: UUID, - data: schemas.RecordingUpdate, -): - """Update a recording.""" - recording = await api.recordings.get(session, recording_uuid) - response = await api.recordings.update(session, recording, data) - await session.commit() - return response - - -@recording_router.post( - "/detail/tags/", - response_model=schemas.Recording, - response_model_exclude_none=True, -) -async def add_recording_tag( - session: Session, - recording_uuid: UUID, - key: str, - value: str, -): - """Add a tag to a recording.""" - recording = await api.recordings.get(session, recording_uuid) - tag = await api.tags.get(session, (key, value)) - response = await api.recordings.add_tag(session, recording, tag) - await session.commit() - return response - - -@recording_router.delete( - "/detail/tags/", - response_model=schemas.Recording, - response_model_exclude_none=True, -) -async def remove_recording_tag( - session: Session, - recording_uuid: UUID, - key: str, - value: str, -): - """Remove a tag from a recording.""" - recording = await api.recordings.get(session, recording_uuid) - tag = await api.tags.get(session, (key, value)) - response = await api.recordings.remove_tag(session, recording, tag) - await session.commit() - return response - - -@recording_router.post( - "/detail/notes/", - response_model=schemas.Recording, - response_model_exclude_none=True, -) -async def add_recording_note( - session: Session, - recording_uuid: UUID, - data: schemas.NoteCreate, - user: ActiveUser, -): - """Add a note to a recording.""" - recording = await api.recordings.get(session, recording_uuid) - note = await api.notes.create( - session, - message=data.message, - is_issue=data.is_issue, - created_by=user, + recording_router = APIRouter() + + @recording_router.get( + "/", + response_model=schemas.Page[schemas.Recording], + response_model_exclude_none=True, + ) + async def get_recordings( + session: Session, + limit: Limit = 10, + offset: Offset = 0, + sort_by: str = "-created_on", + filter: RecordingFilter = Depends(RecordingFilter), # type: ignore + ): + """Get a page of datasets.""" + datasets, total = await api.recordings.get_many( + session, + limit=limit, + offset=offset, + filters=[filter], + sort_by=sort_by, + ) + return schemas.Page( + items=datasets, + total=total, + offset=offset, + limit=limit, + ) + + @recording_router.get( + "/detail/", + response_model=schemas.Recording, + response_model_exclude_none=True, + ) + async def get_recording( + session: Session, + recording_uuid: UUID, + ): + """Get a recording.""" + return await api.recordings.get(session, recording_uuid) + + @recording_router.patch( + "/detail/", + response_model=schemas.Recording, + response_model_exclude_none=True, + ) + async def update_recording( + session: Session, + recording_uuid: UUID, + data: schemas.RecordingUpdate, + ): + """Update a recording.""" + recording = await api.recordings.get(session, recording_uuid) + response = await api.recordings.update(session, recording, data) + await session.commit() + return response + + @recording_router.post( + "/detail/tags/", + response_model=schemas.Recording, + response_model_exclude_none=True, + ) + async def add_recording_tag( + session: Session, + recording_uuid: UUID, + key: str, + value: str, + ): + """Add a tag to a recording.""" + recording = await api.recordings.get(session, recording_uuid) + tag = await api.tags.get(session, (key, value)) + response = await api.recordings.add_tag(session, recording, tag) + await session.commit() + return response + + @recording_router.delete( + "/detail/tags/", + response_model=schemas.Recording, + response_model_exclude_none=True, + ) + async def remove_recording_tag( + session: Session, + recording_uuid: UUID, + key: str, + value: str, + ): + """Remove a tag from a recording.""" + recording = await api.recordings.get(session, recording_uuid) + tag = await api.tags.get(session, (key, value)) + response = await api.recordings.remove_tag(session, recording, tag) + await session.commit() + return response + + @recording_router.post( + "/detail/notes/", + response_model=schemas.Recording, + response_model_exclude_none=True, + ) + async def add_recording_note( + session: Session, + recording_uuid: UUID, + data: schemas.NoteCreate, + user: schemas.SimpleUser = Depends(active_user), + ): + """Add a note to a recording.""" + recording = await api.recordings.get(session, recording_uuid) + note = await api.notes.create( + session, + message=data.message, + is_issue=data.is_issue, + created_by=user, + ) + response = await api.recordings.add_note(session, recording, note) + await session.commit() + return response + + @recording_router.delete( + "/detail/notes/", + response_model=schemas.Recording, + response_model_exclude_none=True, + ) + async def remove_recording_note( + session: Session, + recording_uuid: UUID, + note_uuid: UUID, + ): + """Remove a note from a recording.""" + recording = await api.recordings.get(session, recording_uuid) + note = await api.notes.get(session, note_uuid) + response = await api.recordings.remove_note(session, recording, note) + await session.commit() + return response + + @recording_router.post( + "/detail/features/", + response_model=schemas.Recording, + response_model_exclude_none=True, + ) + async def add_recording_feature( + session: Session, + recording_uuid: UUID, + name: str, + value: float, + ): + """Add a feature to a recording.""" + recording = await api.recordings.get(session, recording_uuid) + feature = await api.features.get_feature( + session, name=name, value=value + ) + response = await api.recordings.add_feature( + session, recording, feature + ) + await session.commit() + return response + + @recording_router.delete( + "/detail/features/", + response_model=schemas.Recording, + response_model_exclude_none=True, + ) + async def remove_recording_feature( + session: Session, + recording_uuid: UUID, + name: str, + value: float, + ): + """Remove a feature from a recording.""" + recording = await api.recordings.get(session, recording_uuid) + feature = await api.features.get_feature( + session, name=name, value=value + ) + response = await api.recordings.remove_feature( + session, recording, feature + ) + await session.commit() + return response + + @recording_router.patch( + "/detail/features/", + response_model=schemas.Recording, + response_model_exclude_none=True, ) - response = await api.recordings.add_note(session, recording, note) - await session.commit() - return response - - -@recording_router.delete( - "/detail/notes/", - response_model=schemas.Recording, - response_model_exclude_none=True, -) -async def remove_recording_note( - session: Session, - recording_uuid: UUID, - note_uuid: UUID, -): - """Remove a note from a recording.""" - recording = await api.recordings.get(session, recording_uuid) - note = await api.notes.get(session, note_uuid) - response = await api.recordings.remove_note(session, recording, note) - await session.commit() - return response - - -@recording_router.post( - "/detail/features/", - response_model=schemas.Recording, - response_model_exclude_none=True, -) -async def add_recording_feature( - session: Session, - recording_uuid: UUID, - name: str, - value: float, -): - """Add a feature to a recording.""" - recording = await api.recordings.get(session, recording_uuid) - feature = await api.features.get_feature(session, name=name, value=value) - response = await api.recordings.add_feature(session, recording, feature) - await session.commit() - return response - - -@recording_router.delete( - "/detail/features/", - response_model=schemas.Recording, - response_model_exclude_none=True, -) -async def remove_recording_feature( - session: Session, - recording_uuid: UUID, - name: str, - value: float, -): - """Remove a feature from a recording.""" - recording = await api.recordings.get(session, recording_uuid) - feature = await api.features.get_feature(session, name=name, value=value) - response = await api.recordings.remove_feature(session, recording, feature) - await session.commit() - return response - - -@recording_router.patch( - "/detail/features/", - response_model=schemas.Recording, - response_model_exclude_none=True, -) -async def update_recording_feature( - session: Session, - recording_uuid: UUID, - name: str, - value: float, -): - """Update a feature on a recording.""" - recording = await api.recordings.get(session, recording_uuid) - feature = await api.features.get_feature(session, name=name, value=value) - response = await api.recordings.update_feature( - session, - recording, - feature, + async def update_recording_feature( + session: Session, + recording_uuid: UUID, + name: str, + value: float, + ): + """Update a feature on a recording.""" + recording = await api.recordings.get(session, recording_uuid) + feature = await api.features.get_feature( + session, name=name, value=value + ) + response = await api.recordings.update_feature( + session, + recording, + feature, + ) + await session.commit() + return response + + @recording_router.delete( + "/detail/", + response_model=schemas.Recording, + response_model_exclude_none=True, ) - await session.commit() - return response - - -@recording_router.delete( - "/detail/", - response_model=schemas.Recording, - response_model_exclude_none=True, -) -async def delete_recording( - session: Session, - recording_uuid: UUID, -): - """Delete a recording.""" - recording = await api.recordings.get(session, recording_uuid) - await api.recordings.delete(session, recording) - await session.commit() - return recording + async def delete_recording( + session: Session, + recording_uuid: UUID, + ): + """Delete a recording.""" + recording = await api.recordings.get(session, recording_uuid) + await api.recordings.delete(session, recording) + await session.commit() + return recording + + return recording_router diff --git a/back/src/whombat/routes/sound_event_annotations.py b/back/src/whombat/routes/sound_event_annotations.py index d9dcab80..0eaddcee 100644 --- a/back/src/whombat/routes/sound_event_annotations.py +++ b/back/src/whombat/routes/sound_event_annotations.py @@ -1,4 +1,5 @@ """REST API routes for sound_event_annotations.""" + from uuid import UUID from fastapi import APIRouter, Depends @@ -9,276 +10,275 @@ get_scatterplot_data, ) from whombat.filters.sound_event_annotations import SoundEventAnnotationFilter -from whombat.routes.dependencies import ActiveUser, Session +from whombat.routes.dependencies import Session, get_current_user_dependency +from whombat.routes.dependencies.settings import WhombatSettings from whombat.routes.types import Limit, Offset __all__ = [ - "sound_event_annotations_router", + "get_sound_event_annotations_router", ] -sound_event_annotations_router = APIRouter() - +def get_sound_event_annotations_router(settings: WhombatSettings) -> APIRouter: + """Get the API router for sound_event_annotations.""" -@sound_event_annotations_router.post( - "/", - response_model=schemas.SoundEventAnnotation, -) -async def create_annotation( - session: Session, - clip_annotation_uuid: UUID, - data: schemas.SoundEventAnnotationCreate, - user: ActiveUser, -): - """Create annotation.""" - clip_annotation = await api.clip_annotations.get( - session, - clip_annotation_uuid, - ) + active_user = get_current_user_dependency(settings) - # Create the corresponding sound event - sound_event = await api.sound_events.create( - session, - recording=clip_annotation.clip.recording, - geometry=data.geometry, - ) + sound_event_annotations_router = APIRouter() - # Create the annotation - sound_event_annotation = await api.sound_event_annotations.create( - session, - sound_event=sound_event, - clip_annotation=clip_annotation, - created_by=user, + @sound_event_annotations_router.post( + "/", + response_model=schemas.SoundEventAnnotation, ) + async def create_annotation( + session: Session, + clip_annotation_uuid: UUID, + data: schemas.SoundEventAnnotationCreate, + user: schemas.SimpleUser = Depends(active_user), + ): + """Create annotation.""" - for tag in data.tags: - tag = await api.tags.get(session, (tag.key, tag.value)) - sound_event_annotation = await api.sound_event_annotations.add_tag( + clip_annotation = await api.clip_annotations.get( session, - sound_event_annotation, - tag, - user, + clip_annotation_uuid, ) - await session.commit() - return sound_event_annotation + # Create the corresponding sound event + sound_event = await api.sound_events.create( + session, + recording=clip_annotation.clip.recording, + geometry=data.geometry, + ) + # Create the annotation + sound_event_annotation = await api.sound_event_annotations.create( + session, + sound_event=sound_event, + clip_annotation=clip_annotation, + created_by=user, + ) -@sound_event_annotations_router.get( - "/detail/", - response_model=schemas.SoundEventAnnotation, -) -async def get_annotation( - session: Session, - sound_event_annotation_uuid: UUID, -): - """Get an annotation.""" - return await api.sound_event_annotations.get( - session, - sound_event_annotation_uuid, - ) + for tag in data.tags: + tag = await api.tags.get(session, (tag.key, tag.value)) + sound_event_annotation = await api.sound_event_annotations.add_tag( + session, + sound_event_annotation, + tag, + user, + ) + await session.commit() + return sound_event_annotation -@sound_event_annotations_router.get( - "/", - response_model=schemas.Page[schemas.SoundEventAnnotation], -) -async def get_sound_event_annotations( - session: Session, - limit: Limit = 10, - offset: Offset = 0, - filter: SoundEventAnnotationFilter = Depends(SoundEventAnnotationFilter), # type: ignore - sort_by: str = "-created_on", -): - """Get a page of annotation sound_event_annotations.""" - ( - sound_event_annotations, - total, - ) = await api.sound_event_annotations.get_many( - session, - limit=limit, - offset=offset, - filters=[filter], - sort_by=sort_by, + @sound_event_annotations_router.get( + "/detail/", + response_model=schemas.SoundEventAnnotation, ) - return schemas.Page( - items=sound_event_annotations, - total=total, - limit=limit, - offset=offset, - ) - + async def get_annotation( + session: Session, + sound_event_annotation_uuid: UUID, + ): + """Get an annotation.""" + return await api.sound_event_annotations.get( + session, + sound_event_annotation_uuid, + ) -@sound_event_annotations_router.patch( - "/detail/", - response_model=schemas.SoundEventAnnotation, -) -async def update_annotation( - session: Session, - sound_event_annotation_uuid: UUID, - data: schemas.SoundEventUpdate, -): - """Update an annotation.""" - sound_event_annotation = await api.sound_event_annotations.get( - session, - sound_event_annotation_uuid, + @sound_event_annotations_router.get( + "/", + response_model=schemas.Page[schemas.SoundEventAnnotation], ) - sound_event_annotation.sound_event = await api.sound_events.update( - session, - sound_event_annotation.sound_event, - data, - ) - await session.commit() - return sound_event_annotation - + async def get_sound_event_annotations( + session: Session, + limit: Limit = 10, + offset: Offset = 0, + filter: SoundEventAnnotationFilter = Depends(SoundEventAnnotationFilter), # type: ignore + sort_by: str = "-created_on", + ): + """Get a page of annotation sound_event_annotations.""" + ( + sound_event_annotations, + total, + ) = await api.sound_event_annotations.get_many( + session, + limit=limit, + offset=offset, + filters=[filter], + sort_by=sort_by, + ) + return schemas.Page( + items=sound_event_annotations, + total=total, + limit=limit, + offset=offset, + ) -@sound_event_annotations_router.delete( - "/detail/", - response_model=schemas.SoundEventAnnotation, -) -async def delete_annotation( - session: Session, - sound_event_annotation_uuid: UUID, -): - """Remove a clip from an annotation project.""" - sound_event_annotation = await api.sound_event_annotations.get( - session, - sound_event_annotation_uuid, + @sound_event_annotations_router.patch( + "/detail/", + response_model=schemas.SoundEventAnnotation, ) - sound_event_annotaiton = await api.sound_event_annotations.delete( - session, - sound_event_annotation, - ) - await session.commit() - return sound_event_annotaiton - + async def update_annotation( + session: Session, + sound_event_annotation_uuid: UUID, + data: schemas.SoundEventUpdate, + ): + """Update an annotation.""" + sound_event_annotation = await api.sound_event_annotations.get( + session, + sound_event_annotation_uuid, + ) + sound_event_annotation.sound_event = await api.sound_events.update( + session, + sound_event_annotation.sound_event, + data, + ) + await session.commit() + return sound_event_annotation -@sound_event_annotations_router.post( - "/detail/tags/", - response_model=schemas.SoundEventAnnotation, -) -async def add_annotation_tag( - session: Session, - sound_event_annotation_uuid: UUID, - key: str, - value: str, - user: ActiveUser, -): - """Add a tag to an annotation annotation.""" - sound_event_annotation = await api.sound_event_annotations.get( - session, - sound_event_annotation_uuid, - ) - tag = await api.tags.get(session, (key, value)) - sound_event_annotation = await api.sound_event_annotations.add_tag( - session, - sound_event_annotation, - tag, - user, + @sound_event_annotations_router.delete( + "/detail/", + response_model=schemas.SoundEventAnnotation, ) - await session.commit() - return sound_event_annotation - + async def delete_annotation( + session: Session, + sound_event_annotation_uuid: UUID, + ): + """Remove a clip from an annotation project.""" + sound_event_annotation = await api.sound_event_annotations.get( + session, + sound_event_annotation_uuid, + ) + sound_event_annotaiton = await api.sound_event_annotations.delete( + session, + sound_event_annotation, + ) + await session.commit() + return sound_event_annotaiton -@sound_event_annotations_router.delete( - "/detail/tags/", - response_model=schemas.SoundEventAnnotation, -) -async def remove_annotation_tag( - session: Session, - sound_event_annotation_uuid: UUID, - key: str, - value: str, -): - """Remove a tag from an annotation annotation.""" - sound_event_annotation = await api.sound_event_annotations.get( - session, - sound_event_annotation_uuid, + @sound_event_annotations_router.post( + "/detail/tags/", + response_model=schemas.SoundEventAnnotation, ) - tag = await api.tags.get(session, (key, value)) - sound_event_annotation = await api.sound_event_annotations.remove_tag( - session, - sound_event_annotation, - tag, - ) - await session.commit() - return sound_event_annotation - + async def add_annotation_tag( + session: Session, + sound_event_annotation_uuid: UUID, + key: str, + value: str, + user: schemas.SimpleUser = Depends(active_user), + ): + """Add a tag to an annotation annotation.""" + sound_event_annotation = await api.sound_event_annotations.get( + session, + sound_event_annotation_uuid, + ) + tag = await api.tags.get(session, (key, value)) + sound_event_annotation = await api.sound_event_annotations.add_tag( + session, + sound_event_annotation, + tag, + user, + ) + await session.commit() + return sound_event_annotation -@sound_event_annotations_router.post( - "/detail/notes/", - response_model=schemas.SoundEventAnnotation, -) -async def create_annotation_note( - session: Session, - sound_event_annotation_uuid: UUID, - data: schemas.NoteCreate, - user: ActiveUser, -): - """Create a note for an annotation annotation.""" - sound_event_annotation = await api.sound_event_annotations.get( - session, - sound_event_annotation_uuid, + @sound_event_annotations_router.delete( + "/detail/tags/", + response_model=schemas.SoundEventAnnotation, ) - note = await api.notes.create( - session, - message=data.message, - is_issue=data.is_issue, - created_by=user, - ) - sound_event_annotation = await api.sound_event_annotations.add_note( - session, - sound_event_annotation, - note, - ) - await session.commit() - return sound_event_annotation - + async def remove_annotation_tag( + session: Session, + sound_event_annotation_uuid: UUID, + key: str, + value: str, + ): + """Remove a tag from an annotation annotation.""" + sound_event_annotation = await api.sound_event_annotations.get( + session, + sound_event_annotation_uuid, + ) + tag = await api.tags.get(session, (key, value)) + sound_event_annotation = await api.sound_event_annotations.remove_tag( + session, + sound_event_annotation, + tag, + ) + await session.commit() + return sound_event_annotation -@sound_event_annotations_router.delete( - "/detail/notes/", - response_model=schemas.SoundEventAnnotation, -) -async def delete_annotation_note( - session: Session, - sound_event_annotation_uuid: UUID, - note_uuid: UUID, -): - """Delete a note from an annotation annotation.""" - sound_event_annotation = await api.sound_event_annotations.get( - session, - sound_event_annotation_uuid, + @sound_event_annotations_router.post( + "/detail/notes/", + response_model=schemas.SoundEventAnnotation, ) - note = await api.notes.get(session, note_uuid) - sound_event_annotation = await api.sound_event_annotations.remove_note( - session, - sound_event_annotation, - note, - ) - await session.commit() - return sound_event_annotation - + async def create_annotation_note( + session: Session, + sound_event_annotation_uuid: UUID, + data: schemas.NoteCreate, + user: schemas.SimpleUser = Depends(active_user), + ): + """Create a note for an annotation annotation.""" + sound_event_annotation = await api.sound_event_annotations.get( + session, + sound_event_annotation_uuid, + ) + note = await api.notes.create( + session, + message=data.message, + is_issue=data.is_issue, + created_by=user, + ) + sound_event_annotation = await api.sound_event_annotations.add_note( + session, + sound_event_annotation, + note, + ) + await session.commit() + return sound_event_annotation -@sound_event_annotations_router.get( - "/scatter_plot/", - response_model=schemas.Page[ScatterPlotData], -) -async def get_scatter_plot_data( - session: Session, - limit: Limit = 1000, - offset: Offset = 0, - filter: SoundEventAnnotationFilter = Depends(SoundEventAnnotationFilter), # type: ignore -): - items, count = await get_scatterplot_data( - session, - limit=limit, - offset=offset, - filters=[filter], + @sound_event_annotations_router.delete( + "/detail/notes/", + response_model=schemas.SoundEventAnnotation, ) + async def delete_annotation_note( + session: Session, + sound_event_annotation_uuid: UUID, + note_uuid: UUID, + ): + """Delete a note from an annotation annotation.""" + sound_event_annotation = await api.sound_event_annotations.get( + session, + sound_event_annotation_uuid, + ) + note = await api.notes.get(session, note_uuid) + sound_event_annotation = await api.sound_event_annotations.remove_note( + session, + sound_event_annotation, + note, + ) + await session.commit() + return sound_event_annotation - return schemas.Page( - items=items, - total=count, - limit=limit, - offset=offset, + @sound_event_annotations_router.get( + "/scatter_plot/", + response_model=schemas.Page[ScatterPlotData], ) + async def get_scatter_plot_data( + session: Session, + limit: Limit = 1000, + offset: Offset = 0, + filter: SoundEventAnnotationFilter = Depends(SoundEventAnnotationFilter), # type: ignore + ): + items, count = await get_scatterplot_data( + session, + limit=limit, + offset=offset, + filters=[filter], + ) + + return schemas.Page( + items=items, + total=count, + limit=limit, + offset=offset, + ) + + return sound_event_annotations_router diff --git a/back/src/whombat/routes/sound_event_evaluations.py b/back/src/whombat/routes/sound_event_evaluations.py index 8f65dee2..66d22daa 100644 --- a/back/src/whombat/routes/sound_event_evaluations.py +++ b/back/src/whombat/routes/sound_event_evaluations.py @@ -1,4 +1,5 @@ """REST API routes for Sound Event Predictions.""" + from fastapi import APIRouter, Depends from whombat import api, schemas diff --git a/back/src/whombat/routes/sound_event_predictions.py b/back/src/whombat/routes/sound_event_predictions.py index b7c0c45d..127d6785 100644 --- a/back/src/whombat/routes/sound_event_predictions.py +++ b/back/src/whombat/routes/sound_event_predictions.py @@ -1,4 +1,5 @@ """REST API routes for Sound Event Predictions.""" + from uuid import UUID from fastapi import APIRouter, Depends diff --git a/back/src/whombat/routes/sound_events.py b/back/src/whombat/routes/sound_events.py index 87a5ba70..54dff7f6 100644 --- a/back/src/whombat/routes/sound_events.py +++ b/back/src/whombat/routes/sound_events.py @@ -1,4 +1,5 @@ """REST API routes for sound events.""" + from uuid import UUID from fastapi import APIRouter, Depends diff --git a/back/src/whombat/routes/spectrograms.py b/back/src/whombat/routes/spectrograms.py index 865a376a..66f6a25a 100644 --- a/back/src/whombat/routes/spectrograms.py +++ b/back/src/whombat/routes/spectrograms.py @@ -1,4 +1,5 @@ """REST API routes for spectrograms.""" + from uuid import UUID from fastapi import APIRouter, Depends, Response diff --git a/back/src/whombat/routes/tags.py b/back/src/whombat/routes/tags.py index fd7d888b..30fb4cdf 100644 --- a/back/src/whombat/routes/tags.py +++ b/back/src/whombat/routes/tags.py @@ -1,4 +1,5 @@ """REST API routes for tags.""" + from fastapi import APIRouter, Depends from whombat import api, schemas diff --git a/back/src/whombat/routes/types.py b/back/src/whombat/routes/types.py index 7abcc27c..4ffa74a1 100644 --- a/back/src/whombat/routes/types.py +++ b/back/src/whombat/routes/types.py @@ -1,4 +1,5 @@ """Common types for the API.""" + from typing import Annotated from fastapi import Query diff --git a/back/src/whombat/routes/user_runs.py b/back/src/whombat/routes/user_runs.py index 756028ca..0347faae 100644 --- a/back/src/whombat/routes/user_runs.py +++ b/back/src/whombat/routes/user_runs.py @@ -1,73 +1,78 @@ """REST API routes for model runs.""" + from uuid import UUID from fastapi import APIRouter, Depends from whombat import api, schemas from whombat.filters.user_runs import UserRunFilter -from whombat.routes.dependencies import ActiveUser, Session +from whombat.routes.dependencies import Session, get_current_user_dependency +from whombat.routes.dependencies.settings import WhombatSettings from whombat.routes.types import Limit, Offset __all__ = [ - "user_runs_router", + "get_user_runs_router", ] -user_runs_router = APIRouter() - +def get_user_runs_router(settings: WhombatSettings) -> APIRouter: + """Get the API router for model runs.""" -@user_runs_router.get("/", response_model=schemas.Page[schemas.UserRun]) -async def get_user_runs( - session: Session, - limit: Limit = 100, - offset: Offset = 0, - filter: UserRunFilter = Depends(UserRunFilter), # type: ignore -) -> schemas.Page[schemas.UserRun]: - """Get list of model runs.""" - user_runs, total = await api.user_runs.get_many( - session, - limit=limit, - offset=offset, - filters=[filter], - ) - return schemas.Page( - items=user_runs, - total=total, - offset=offset, - limit=limit, - ) + active_user = get_current_user_dependency(settings) + user_runs_router = APIRouter() -@user_runs_router.post("/", response_model=schemas.UserRun) -async def create_user_run( - session: Session, - user: ActiveUser, -) -> schemas.UserRun: - """Create model run.""" - user_run = await api.user_runs.create( - session, - user=user, - ) - await session.commit() - return user_run + @user_runs_router.get("/", response_model=schemas.Page[schemas.UserRun]) + async def get_user_runs( + session: Session, + limit: Limit = 100, + offset: Offset = 0, + filter: UserRunFilter = Depends(UserRunFilter), # type: ignore + ) -> schemas.Page[schemas.UserRun]: + """Get list of model runs.""" + user_runs, total = await api.user_runs.get_many( + session, + limit=limit, + offset=offset, + filters=[filter], + ) + return schemas.Page( + items=user_runs, + total=total, + offset=offset, + limit=limit, + ) + @user_runs_router.post("/", response_model=schemas.UserRun) + async def create_user_run( + session: Session, + user: schemas.SimpleUser = Depends(active_user), + ) -> schemas.UserRun: + """Create model run.""" + user_run = await api.user_runs.create( + session, + user=user, + ) + await session.commit() + return user_run -@user_runs_router.get("/detail/", response_model=schemas.UserRun) -async def get_user_run( - session: Session, - user_run_uuid: UUID, -) -> schemas.UserRun: - """Get model run.""" - return await api.user_runs.get(session, user_run_uuid) + @user_runs_router.get("/detail/", response_model=schemas.UserRun) + async def get_user_run( + session: Session, + user_run_uuid: UUID, + ) -> schemas.UserRun: + """Get model run.""" + return await api.user_runs.get(session, user_run_uuid) + @user_runs_router.delete("/detail/", response_model=schemas.UserRun) + async def delete_user_run( + session: Session, + user_run_uuid: UUID, + ) -> schemas.UserRun: + """Delete model run.""" + user_run = await api.user_runs.get(session, user_run_uuid) + await api.user_runs.delete(session, user_run) + await session.commit() + return user_run -@user_runs_router.delete("/detail/", response_model=schemas.UserRun) -async def delete_user_run( - session: Session, - user_run_uuid: UUID, -) -> schemas.UserRun: - """Delete model run.""" - user_run = await api.user_runs.get(session, user_run_uuid) - await api.user_runs.delete(session, user_run) - await session.commit() - return user_run + return user_runs_router diff --git a/back/src/whombat/routes/users.py b/back/src/whombat/routes/users.py index fe435000..7cffbf3c 100644 --- a/back/src/whombat/routes/users.py +++ b/back/src/whombat/routes/users.py @@ -1,4 +1,5 @@ """Module containing the router for the Auth.""" + from fastapi import APIRouter, Depends, HTTPException, status from whombat.routes.dependencies.auth import get_users_api diff --git a/back/src/whombat/schemas/evaluations.py b/back/src/whombat/schemas/evaluations.py index eb27acd3..488af57d 100644 --- a/back/src/whombat/schemas/evaluations.py +++ b/back/src/whombat/schemas/evaluations.py @@ -1,4 +1,5 @@ """Schemas for Evaluations.""" + from uuid import UUID from pydantic import BaseModel, Field diff --git a/back/src/whombat/schemas/model_runs.py b/back/src/whombat/schemas/model_runs.py index 670fda4c..0f035369 100644 --- a/back/src/whombat/schemas/model_runs.py +++ b/back/src/whombat/schemas/model_runs.py @@ -1,4 +1,5 @@ """Schemas for handling Prediction Runs.""" + from uuid import UUID from pydantic import Field diff --git a/back/src/whombat/schemas/plugin.py b/back/src/whombat/schemas/plugin.py index 6bf448d9..095f0a9b 100644 --- a/back/src/whombat/schemas/plugin.py +++ b/back/src/whombat/schemas/plugin.py @@ -1,6 +1,5 @@ """Schemas for handling Plugin information.""" - from pydantic import BaseModel, Field __all__ = [ diff --git a/back/src/whombat/schemas/tags.py b/back/src/whombat/schemas/tags.py index 541b4d52..57a9f0f8 100644 --- a/back/src/whombat/schemas/tags.py +++ b/back/src/whombat/schemas/tags.py @@ -1,4 +1,5 @@ """Schemas for handling Tags.""" + from pydantic import BaseModel, Field from whombat.schemas.base import BaseSchema diff --git a/back/src/whombat/schemas/user_runs.py b/back/src/whombat/schemas/user_runs.py index ec5246d1..bb2f33e3 100644 --- a/back/src/whombat/schemas/user_runs.py +++ b/back/src/whombat/schemas/user_runs.py @@ -1,4 +1,5 @@ """Schemas for handling User Runs.""" + from uuid import UUID, uuid4 from pydantic import BaseModel, Field diff --git a/back/src/whombat/schemas/users.py b/back/src/whombat/schemas/users.py index 9284022f..8d96c0de 100644 --- a/back/src/whombat/schemas/users.py +++ b/back/src/whombat/schemas/users.py @@ -17,9 +17,9 @@ class SimpleUser(BaseSchema): username: str email: EmailStr | None = None name: str | None = None - is_active: bool - is_superuser: bool - is_verified: bool + is_active: bool | None = False + is_superuser: bool | None = False + is_verified: bool | None = False class User(schemas.BaseUser[uuid.UUID]): diff --git a/back/src/whombat/system/app.py b/back/src/whombat/system/app.py index a01c72e2..c089d193 100644 --- a/back/src/whombat/system/app.py +++ b/back/src/whombat/system/app.py @@ -1,4 +1,5 @@ """System module for Whombat.""" + import functools from contextlib import asynccontextmanager from pathlib import Path diff --git a/back/src/whombat/system/auth.py b/back/src/whombat/system/auth.py index a716eab9..bd851fba 100644 --- a/back/src/whombat/system/auth.py +++ b/back/src/whombat/system/auth.py @@ -1,4 +1,5 @@ """Authentication dependencies.""" + from fastapi_users.authentication import AuthenticationBackend, CookieTransport from fastapi_users.authentication.strategy.db import ( AccessTokenDatabase, @@ -33,12 +34,8 @@ def get_database_strategy( ) -def get_auth_backend( - settings: Settings, - get_strategy, -) -> AuthenticationBackend: - """Get the authentication backend.""" - cookie_transport = CookieTransport( +def get_cookie_transport(settings: Settings): + return CookieTransport( cookie_max_age=3600, cookie_name="whombatauth", cookie_secure=False, @@ -46,6 +43,13 @@ def get_auth_backend( cookie_samesite="lax", ) + +def get_auth_backend( + settings: Settings, + get_strategy, +) -> AuthenticationBackend: + """Get the authentication backend.""" + cookie_transport = get_cookie_transport(settings) return AuthenticationBackend( name="database", transport=cookie_transport, diff --git a/back/src/whombat/system/data.py b/back/src/whombat/system/data.py index ad43f158..35e1ad7a 100644 --- a/back/src/whombat/system/data.py +++ b/back/src/whombat/system/data.py @@ -1,4 +1,5 @@ """Whombat Data management.""" + import os import sys from pathlib import Path diff --git a/back/src/whombat/system/database.py b/back/src/whombat/system/database.py index e77fa319..22b566ee 100644 --- a/back/src/whombat/system/database.py +++ b/back/src/whombat/system/database.py @@ -1,4 +1,5 @@ """Function to initialize the database.""" + import logging from contextlib import asynccontextmanager from enum import Enum diff --git a/back/src/whombat/system/settings.py b/back/src/whombat/system/settings.py index 361b1f7e..c9ef461d 100644 --- a/back/src/whombat/system/settings.py +++ b/back/src/whombat/system/settings.py @@ -2,6 +2,7 @@ We are using pydantic to define our settings. """ + import warnings from functools import lru_cache from pathlib import Path diff --git a/back/src/whombat/system/users.py b/back/src/whombat/system/users.py index 8854a7fc..e3b72475 100644 --- a/back/src/whombat/system/users.py +++ b/back/src/whombat/system/users.py @@ -1,4 +1,5 @@ """User Management.""" + from uuid import UUID from fastapi.security import OAuth2PasswordRequestForm diff --git a/back/tests/conftest.py b/back/tests/conftest.py index 39afd7c7..2a625937 100644 --- a/back/tests/conftest.py +++ b/back/tests/conftest.py @@ -312,10 +312,12 @@ async def clip_annotation( clip: schemas.Clip, ) -> schemas.ClipAnnotation: """Create a clip annotation for testing.""" - return await api.clip_annotations.create( + ann = await api.clip_annotations.create( session, clip=clip, ) + await session.commit() + return ann @pytest.fixture diff --git a/back/tests/test_api/test_annotation_projects.py b/back/tests/test_api/test_annotation_projects.py index 143d3f94..572bc078 100644 --- a/back/tests/test_api/test_annotation_projects.py +++ b/back/tests/test_api/test_annotation_projects.py @@ -1,4 +1,5 @@ """Test suite for annotation project API.""" + from uuid import uuid4 import pytest diff --git a/back/tests/test_api/test_clips.py b/back/tests/test_api/test_clips.py index 00c831cf..c13f39e8 100644 --- a/back/tests/test_api/test_clips.py +++ b/back/tests/test_api/test_clips.py @@ -1,4 +1,5 @@ """Test suite for API clip functions.""" + from uuid import uuid4 import pytest diff --git a/back/tests/test_api/test_notes.py b/back/tests/test_api/test_notes.py index 5a7cd872..5f7747ec 100644 --- a/back/tests/test_api/test_notes.py +++ b/back/tests/test_api/test_notes.py @@ -1,4 +1,5 @@ """Test suite for the notes Python API module.""" + from uuid import uuid4 import pytest diff --git a/back/tests/test_api/test_recordings.py b/back/tests/test_api/test_recordings.py index 7a14e27e..8557c11d 100644 --- a/back/tests/test_api/test_recordings.py +++ b/back/tests/test_api/test_recordings.py @@ -1,4 +1,5 @@ """Test suite for the notes Python API module.""" + import datetime import shutil from collections.abc import Callable diff --git a/back/tests/test_api/test_sound_events.py b/back/tests/test_api/test_sound_events.py index 0362509a..6278ac19 100644 --- a/back/tests/test_api/test_sound_events.py +++ b/back/tests/test_api/test_sound_events.py @@ -1,4 +1,5 @@ """Test suit for the Sound Events Python API module.""" + from uuid import UUID from soundevent.data import geometries diff --git a/back/tests/test_api/test_users.py b/back/tests/test_api/test_users.py index 672a57f0..24d4fe81 100644 --- a/back/tests/test_api/test_users.py +++ b/back/tests/test_api/test_users.py @@ -1,4 +1,5 @@ """Test the database API module.""" + import uuid import pytest diff --git a/back/tests/test_core/test_files.py b/back/tests/test_core/test_files.py index db3cdf99..dd816d41 100644 --- a/back/tests/test_core/test_files.py +++ b/back/tests/test_core/test_files.py @@ -1,4 +1,5 @@ """Test suite of Whombat core function to manage files.""" + from collections.abc import Callable from pathlib import Path diff --git a/back/tests/test_database/test_database.py b/back/tests/test_database/test_database.py index 95e028f3..6c66c1af 100644 --- a/back/tests/test_database/test_database.py +++ b/back/tests/test_database/test_database.py @@ -1,4 +1,5 @@ """Test suite to check database integrity.""" + import inspect import sqlalchemy diff --git a/back/tests/test_filters/test_recording_filters.py b/back/tests/test_filters/test_recording_filters.py index 4b403ad9..a13bfefa 100644 --- a/back/tests/test_filters/test_recording_filters.py +++ b/back/tests/test_filters/test_recording_filters.py @@ -1,4 +1,5 @@ """Test suite for recording filters.""" + import datetime from pathlib import Path from typing import Callable diff --git a/back/tests/test_routers/conftest.py b/back/tests/test_routers/conftest.py index d6281b17..75af8a5d 100644 --- a/back/tests/test_routers/conftest.py +++ b/back/tests/test_routers/conftest.py @@ -1,14 +1,13 @@ -from pathlib import Path - import pytest from fastapi.testclient import TestClient +from whombat import schemas from whombat.system import create_app from whombat.system.settings import Settings, get_settings @pytest.fixture -async def client(database_path: Path, settings: Settings): +async def client(settings: Settings): """Fixture to initialize the test database.""" app = create_app(settings) @@ -17,4 +16,18 @@ async def client(database_path: Path, settings: Settings): with TestClient(app) as client: yield client - database_path.unlink() + +@pytest.fixture +def cookies(client: TestClient, user: schemas.SimpleUser) -> dict[str, str]: + """Fixture to get the cookies from a logged in user.""" + response = client.post( + "/api/v1/auth/login", + data={ + "username": user.username, + "password": "password", + }, + ) + + assert response.status_code == 204 + name, value = response.headers["set-cookie"].split(";")[0].split("=") + return {name: value} diff --git a/back/tests/test_routers/test_auth.py b/back/tests/test_routers/test_auth.py index 82ec3ac6..c93c2ee1 100644 --- a/back/tests/test_routers/test_auth.py +++ b/back/tests/test_routers/test_auth.py @@ -16,3 +16,18 @@ async def test_admin_can_login(client: TestClient, user: schemas.User): ) assert response.status_code == 204 + + +async def test_can_get_active_user( + client: TestClient, + user: schemas.SimpleUser, + cookies: dict[str, str], +): + """Test that the admin user can login.""" + response = client.get( + "/api/v1/users/me", + cookies=cookies, + ) + + assert response.status_code == 200, response.text + assert schemas.SimpleUser.model_validate(response.json()) == user diff --git a/back/tests/test_routers/test_sound_event_annotations.py b/back/tests/test_routers/test_sound_event_annotations.py new file mode 100644 index 00000000..03ead4e4 --- /dev/null +++ b/back/tests/test_routers/test_sound_event_annotations.py @@ -0,0 +1,26 @@ +"""Test the Sound Event Annotation endpoints.""" + +from fastapi.testclient import TestClient +from soundevent import data + +from whombat import schemas + + +async def test_can_create_a_sound_event_annotation( + client: TestClient, + clip_annotation: schemas.ClipAnnotation, + cookies: dict[str, str], +): + """Test that the admin user can login.""" + bounding_box = data.BoundingBox(coordinates=[0, 1000, 1, 2000]) + + response = client.post( + "/api/v1/sound_event_annotations/", + params={ + "clip_annotation_uuid": str(clip_annotation.uuid), + }, + json={"geometry": bounding_box.model_dump()}, + cookies=cookies, + ) + + assert response.status_code == 200 From 3be9f985ff5a41c30eac392a6a78cfc748420be2 Mon Sep 17 00:00:00 2001 From: Santiago Martinez Date: Thu, 1 Feb 2024 08:46:40 +0000 Subject: [PATCH 20/20] fix: linter issues solved --- back/src/whombat/routes/dependencies/auth.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/back/src/whombat/routes/dependencies/auth.py b/back/src/whombat/routes/dependencies/auth.py index 3b832d10..8a9c4bf6 100644 --- a/back/src/whombat/routes/dependencies/auth.py +++ b/back/src/whombat/routes/dependencies/auth.py @@ -1,13 +1,12 @@ """Authentication dependencies.""" -from typing import Annotated from uuid import UUID from fastapi import Depends from fastapi_users import FastAPIUsers from fastapi_users.authentication import AuthenticationBackend -from whombat import models, schemas +from whombat import models from whombat.routes.dependencies.session import Session from whombat.routes.dependencies.settings import WhombatSettings from whombat.routes.dependencies.users import get_user_manager