From 86375f82d436c04448e954e8477a606b664e0f01 Mon Sep 17 00:00:00 2001 From: Alexander Berger Date: Thu, 30 Nov 2023 11:40:41 -0500 Subject: [PATCH 01/13] Adding first pass at github actions --- .github/workflows/_check-coverage-action.yml | 65 ++++++++++++++++ .github/workflows/_format-lint-action.yml | 24 ++++++ .github/workflows/_run-tests-action.yml | 44 +++++++++++ .github/workflows/coverage.yml | 49 ++++++++++++ .github/workflows/release.yml | 76 +++++++++++++++++++ .github/workflows/style.yml | 13 ++++ .github/workflows/tests.yml | 19 +++++ tests/api/unit/services/parse/__init__.py | 0 .../api/unit/services/parse/batch/__init__.py | 0 tests/db/__init__.py | 0 tests/{api => services}/__init__.py | 0 tests/{api/unit => services/io}/__init__.py | 0 .../services/io/test_read_file_contents.py | 0 .../services => services/parse}/__init__.py | 0 .../io => services/parse/batch}/__init__.py | 0 .../services/parse/batch/conftest.py | 0 .../unit => }/services/parse/batch/const.py | 0 .../services/parse/batch/test_parse.py | 0 18 files changed, 290 insertions(+) create mode 100644 .github/workflows/_check-coverage-action.yml create mode 100644 .github/workflows/_format-lint-action.yml create mode 100644 .github/workflows/_run-tests-action.yml create mode 100644 .github/workflows/coverage.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/style.yml create mode 100644 .github/workflows/tests.yml delete mode 100644 tests/api/unit/services/parse/__init__.py delete mode 100644 tests/api/unit/services/parse/batch/__init__.py delete mode 100644 tests/db/__init__.py rename tests/{api => services}/__init__.py (100%) rename tests/{api/unit => services/io}/__init__.py (100%) rename tests/{api/unit => }/services/io/test_read_file_contents.py (100%) rename tests/{api/unit/services => services/parse}/__init__.py (100%) rename tests/{api/unit/services/io => services/parse/batch}/__init__.py (100%) rename tests/{api/unit => }/services/parse/batch/conftest.py (100%) rename tests/{api/unit => }/services/parse/batch/const.py (100%) rename tests/{api/unit => }/services/parse/batch/test_parse.py (100%) diff --git a/.github/workflows/_check-coverage-action.yml b/.github/workflows/_check-coverage-action.yml new file mode 100644 index 0000000..910936f --- /dev/null +++ b/.github/workflows/_check-coverage-action.yml @@ -0,0 +1,65 @@ +name: 'Test Coverage Definition' +on: + workflow_call: + inputs: + coverage-module: + description: "Module to test coverage for" + type: string + required: true + python-version: + description: Python version to set up' + default: '3.11' + type: string + runner-os: + description: 'Runner OS' + default: 'ubuntu-latest' + type: string + upload-coverage: + description: 'Upload coverage results' + default: true + type: boolean + required-coverage: + description: 'Required coverage percentage' + default: 100 + type: string + show-test-traceback: + description: "Show traceback for failed tests" + type: string + default: "no" +jobs: + run-tests: + runs-on: ${{ inputs.runner-os }} + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ inputs.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ inputs.python-version }} + - name: Install Poetry + run: | + curl -sSL https://install.python-poetry.org | python3 - + if [[ "$RUNNER_OS" == "macOS" ]]; then + echo "/Users/runner/.local/bin:$PATH" >> $GITHUB_PATH + fi + - name: Configure Poetry + run: poetry config virtualenvs.create false + - name: Install dependencies with Poetry + run: poetry install + - name: Test with pytest + run: | + poetry run pytest tests \ + --tb=${{ inputs.show-test-traceback }} \ + --cov=${{ inputs.coverage-module }} \ + --cov-report=term \ + --cov-report=html \ + --cov-fail-under=${{ inputs.required-coverage }} > coverage_report.txt + - name: Upload coverage report + uses: actions/upload-artifact@v3 + with: + name: coverage-report + path: coverage_report.txt + - name: Upload coverage report + uses: actions/upload-artifact@v3 + with: + name: coverage-report-html + path: htmlcov \ No newline at end of file diff --git a/.github/workflows/_format-lint-action.yml b/.github/workflows/_format-lint-action.yml new file mode 100644 index 0000000..58baffe --- /dev/null +++ b/.github/workflows/_format-lint-action.yml @@ -0,0 +1,24 @@ +name: 'Lint Code Definition' +on: + workflow_call: + inputs: + python-version: + description: 'Python version to set up' + required: true + default: '3.9' + type: string +jobs: + format-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ inputs.python-version }} + - name: Install Black and Ruff + run: pip install black ruff + - name: Run Ruff Linter + run: ruff src/ --fix + - name: Run Black Formatter + run: black src/ diff --git a/.github/workflows/_run-tests-action.yml b/.github/workflows/_run-tests-action.yml new file mode 100644 index 0000000..ad82334 --- /dev/null +++ b/.github/workflows/_run-tests-action.yml @@ -0,0 +1,44 @@ +name: 'Python Tests Definition' +on: + workflow_call: + inputs: + python-version: + description: Python version to set up' + required: true + default: '3.9' + type: string + runner-os: + description: 'Runner OS' + required: true + default: 'ubuntu-latest' + type: string + upload-coverage: + description: 'Upload coverage results' + default: true + type: boolean + required-coverage: + description: 'Required coverage percentage' + default: 75 + type: string +jobs: + run-tests: + runs-on: ${{ inputs.runner-os }} + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ inputs.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ inputs.python-version }} + - name: Install Poetry + run: | + curl -sSL https://install.python-poetry.org | python3 - + if [[ "$RUNNER_OS" == "macOS" ]]; then + echo "/Users/runner/.local/bin:$PATH" >> $GITHUB_PATH + fi + - name: Configure Poetry + run: poetry config virtualenvs.create false + - name: Install dependencies with Poetry + run: poetry install + - name: Test with pytest + run: | + poetry run pytest tests diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..4a03441 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,49 @@ +name: Coverage +on: + pull_request: + branches: + - 'main' + push: + branches: + - 'main' +jobs: + check-coverage: + uses: ./.github/workflows/_check-coverage-action.yml + with: + required-coverage: ${{ vars.REQUIRED_COVERAGE }} + coverage-module: "geneweaver.api" + comment-coverage-report: + needs: [ check-coverage ] + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + permissions: + pull-requests: write + steps: + - name: Download coverage report artifact + uses: actions/download-artifact@v3 + with: + name: coverage-report + - name: Read coverage report + id: read-coverage + run: | + echo "COVERAGE_REPORT<> $GITHUB_ENV + cat coverage_report.txt >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + - name: Find Comment + uses: peter-evans/find-comment@v2 + id: fc + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: '### Test Coverage Report' + - name: Create or update comment + uses: peter-evans/create-or-update-comment@v3 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + edit-mode: replace + body: | + ### Test Coverage Report + ``` + ${{ env.COVERAGE_REPORT }} + ``` \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d77e35a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,76 @@ +name: Release to PyPI + +on: + push: + branches: + - 'main' + paths: + - 'pyproject.toml' + +permissions: + contents: write + +jobs: + format-lint: + uses: ./.github/workflows/_format-lint-action.yml + with: + python-version: '3.9' + test: + needs: format-lint + uses: ./.github/workflows/_run-tests-action.yml + with: + python-version: '3.9' + runner-os: 'ubuntu-latest' + upload-coverage: false + required-coverage: ${{ vars.REQUIRED_COVERAGE }} + release: + needs: test + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.9'z + + - name: Install dependencies + run: | + pip install toml + + - name: Check for version change + id: version_check + run: | + # Extract version from pyproject.toml + version=$(python -c "import toml; print(toml.load('pyproject.toml')['tool']['poetry']['version'])") + echo "Version=$version" + echo "version=$version" >> $GITHUB_OUTPUT + + # Check if this version tag already exists + if git rev-parse "v$version" >/dev/null 2>&1; then + echo "Version already released" + echo "should_release=true" >> $GITHUB_OUTPUT + else + echo "New version detected" + echo "should_release=false" >> $GITHUB_OUTPUT + fi + + - name: Determine Release Type + id: release_type + run: | + version=$(python -c "import toml; print(toml.load('pyproject.toml')['tool']['poetry']['version'])") + if [[ $version =~ [a-zA-Z] ]]; then + echo "Pre-release version detected" + echo "prerelease=true" >> $GITHUB_OUTPUT + else + echo "Full release version detected" + echo "prerelease=false" >> $GITHUB_OUTPUT + fi + + - name: Install Poetry + if: ${{ steps.version_check.outputs.should_release }} == 'true' + run: | + curl -sSL https://install.python-poetry.org | python3 - + + # TODO: Add deploy to kubernetes step \ No newline at end of file diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml new file mode 100644 index 0000000..bb5988f --- /dev/null +++ b/.github/workflows/style.yml @@ -0,0 +1,13 @@ +name: Style +on: + pull_request: + branches: + - 'main' + push: + branches: + - 'main' +jobs: + format-lint: + uses: ./.github/workflows/_format-lint-action.yml + with: + python-version: '3.9' \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..e702bf3 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,19 @@ +name: Tests +on: + pull_request: + branches: + - 'main' + push: + branches: + - 'main' +jobs: + test: + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + python-version: ['3.9', '3.10', '3.11'] + uses: ./.github/workflows/_run-tests-action.yml + with: + runner-os: ${{ matrix.os }} + python-version: ${{ matrix.python-version }} + required-coverage: ${{ vars.REQUIRED_COVERAGE }} diff --git a/tests/api/unit/services/parse/__init__.py b/tests/api/unit/services/parse/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/api/unit/services/parse/batch/__init__.py b/tests/api/unit/services/parse/batch/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/db/__init__.py b/tests/db/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/api/__init__.py b/tests/services/__init__.py similarity index 100% rename from tests/api/__init__.py rename to tests/services/__init__.py diff --git a/tests/api/unit/__init__.py b/tests/services/io/__init__.py similarity index 100% rename from tests/api/unit/__init__.py rename to tests/services/io/__init__.py diff --git a/tests/api/unit/services/io/test_read_file_contents.py b/tests/services/io/test_read_file_contents.py similarity index 100% rename from tests/api/unit/services/io/test_read_file_contents.py rename to tests/services/io/test_read_file_contents.py diff --git a/tests/api/unit/services/__init__.py b/tests/services/parse/__init__.py similarity index 100% rename from tests/api/unit/services/__init__.py rename to tests/services/parse/__init__.py diff --git a/tests/api/unit/services/io/__init__.py b/tests/services/parse/batch/__init__.py similarity index 100% rename from tests/api/unit/services/io/__init__.py rename to tests/services/parse/batch/__init__.py diff --git a/tests/api/unit/services/parse/batch/conftest.py b/tests/services/parse/batch/conftest.py similarity index 100% rename from tests/api/unit/services/parse/batch/conftest.py rename to tests/services/parse/batch/conftest.py diff --git a/tests/api/unit/services/parse/batch/const.py b/tests/services/parse/batch/const.py similarity index 100% rename from tests/api/unit/services/parse/batch/const.py rename to tests/services/parse/batch/const.py diff --git a/tests/api/unit/services/parse/batch/test_parse.py b/tests/services/parse/batch/test_parse.py similarity index 100% rename from tests/api/unit/services/parse/batch/test_parse.py rename to tests/services/parse/batch/test_parse.py From 897f24ee88bc086955cfbd0ba2aa0bd7c9008058 Mon Sep 17 00:00:00 2001 From: Alexander Berger Date: Thu, 30 Nov 2023 11:41:15 -0500 Subject: [PATCH 02/13] Adding tests dir to format lint action --- .github/workflows/_format-lint-action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_format-lint-action.yml b/.github/workflows/_format-lint-action.yml index 58baffe..5668aea 100644 --- a/.github/workflows/_format-lint-action.yml +++ b/.github/workflows/_format-lint-action.yml @@ -19,6 +19,6 @@ jobs: - name: Install Black and Ruff run: pip install black ruff - name: Run Ruff Linter - run: ruff src/ --fix + run: ruff src/ tests/ - name: Run Black Formatter - run: black src/ + run: black src/ tests/ From babfb115445c05878e4cc7577a35778f9aeecc5c Mon Sep 17 00:00:00 2001 From: Alexander Berger Date: Mon, 4 Dec 2023 10:11:39 -0500 Subject: [PATCH 03/13] Adding a few init py docstrings --- src/geneweaver/api/__init__.py | 1 + src/geneweaver/api/controller/__init__.py | 1 + src/geneweaver/api/core/__init__.py | 1 + 3 files changed, 3 insertions(+) diff --git a/src/geneweaver/api/__init__.py b/src/geneweaver/api/__init__.py index e69de29..116331e 100644 --- a/src/geneweaver/api/__init__.py +++ b/src/geneweaver/api/__init__.py @@ -0,0 +1 @@ +"""The Geneweaver API Module.""" diff --git a/src/geneweaver/api/controller/__init__.py b/src/geneweaver/api/controller/__init__.py index e69de29..cbee899 100644 --- a/src/geneweaver/api/controller/__init__.py +++ b/src/geneweaver/api/controller/__init__.py @@ -0,0 +1 @@ +"""The API Controller definitions.""" diff --git a/src/geneweaver/api/core/__init__.py b/src/geneweaver/api/core/__init__.py index e69de29..aee463a 100644 --- a/src/geneweaver/api/core/__init__.py +++ b/src/geneweaver/api/core/__init__.py @@ -0,0 +1 @@ +"""Core functionality for the Geneweaver API package.""" From e462ff05a731807752a5f2b8ded6dd39ecfb7b54 Mon Sep 17 00:00:00 2001 From: Alexander Berger Date: Mon, 4 Dec 2023 10:14:40 -0500 Subject: [PATCH 04/13] Style and formatting fixes --- src/geneweaver/api/controller/api.py | 7 ++--- src/geneweaver/api/controller/batch.py | 3 +- src/geneweaver/api/controller/genesets.py | 27 +++++++---------- src/geneweaver/api/core/config.py | 3 +- src/geneweaver/api/core/config_class.py | 8 +++-- src/geneweaver/api/core/db.py | 1 + src/geneweaver/api/core/deps.py | 3 +- src/geneweaver/api/core/exceptions.py | 36 ++++++----------------- src/geneweaver/api/dependencies.py | 13 ++++---- src/geneweaver/api/schemas/auth.py | 3 +- 10 files changed, 38 insertions(+), 66 deletions(-) diff --git a/src/geneweaver/api/controller/api.py b/src/geneweaver/api/controller/api.py index 4530ec8..862307b 100644 --- a/src/geneweaver/api/controller/api.py +++ b/src/geneweaver/api/controller/api.py @@ -3,14 +3,11 @@ This file defines the root API for the GeneWeaver API. It is responsible for defining the FastAPI application and including all other API routers. """ -from fastapi import FastAPI, APIRouter, Security - +from fastapi import APIRouter, FastAPI, Security +from geneweaver.api.controller import batch, genesets from geneweaver.api.core import deps from geneweaver.api.core.config import settings -from geneweaver.api.controller import batch -from geneweaver.api.controller import genesets - app = FastAPI( title="GeneWeaver API", docs_url=f"{settings.API_PREFIX}/docs", diff --git a/src/geneweaver/api/controller/batch.py b/src/geneweaver/api/controller/batch.py index 07a003f..b6a657f 100644 --- a/src/geneweaver/api/controller/batch.py +++ b/src/geneweaver/api/controller/batch.py @@ -1,8 +1,7 @@ """API Controller definition for batch processing.""" from typing import Optional -from fastapi import APIRouter, UploadFile, Security - +from fastapi import APIRouter, Security, UploadFile from geneweaver.api.core import deps from geneweaver.api.schemas.auth import UserInternal from geneweaver.api.schemas.batch import BatchResponse diff --git a/src/geneweaver/api/controller/genesets.py b/src/geneweaver/api/controller/genesets.py index 2973b3b..72c8caf 100644 --- a/src/geneweaver/api/controller/genesets.py +++ b/src/geneweaver/api/controller/genesets.py @@ -1,18 +1,13 @@ """Endpoints related to genesets.""" from typing import Optional -from fastapi import APIRouter, Security, Depends, HTTPException - +from fastapi import APIRouter, Depends, HTTPException, Security from geneweaver.api import dependencies as deps from geneweaver.api.schemas.auth import UserInternal -from geneweaver.db import geneset_value as db_geneset_value -from geneweaver.db import geneset as db_geneset -from geneweaver.db import gene as db_gene -from geneweaver.db.geneset import by_id, by_user_id, is_readable -from geneweaver.db.geneset_value import by_geneset_id -from geneweaver.db.user import by_sso_id from geneweaver.core.schema.geneset import GenesetUpload - +from geneweaver.db import geneset as db_geneset +from geneweaver.db import geneset_value as db_geneset_value +from geneweaver.db.geneset import is_readable router = APIRouter(prefix="/genesets") @@ -20,8 +15,8 @@ @router.get("") def get_visible_genesets( user: UserInternal = Security(deps.full_user), - cursor: Optional[deps.Cursor] = Depends(deps.cursor), -): + cursor: Optional[deps.Cursor] = Depends(deps.cursor), +) -> dict: """Get all visible genesets.""" user_genesets = db_geneset.by_user_id(cursor, user.id) return {"genesets": user_genesets} @@ -32,7 +27,7 @@ def get_geneset( geneset_id: int, user: UserInternal = Security(deps.full_user), cursor: Optional[deps.Cursor] = Depends(deps.cursor), -): +) -> dict: """Get a geneset by ID.""" if not is_readable(cursor, user.id, geneset_id): raise HTTPException(status_code=403, detail="Forbidden") @@ -47,9 +42,7 @@ def upload_geneset( geneset: GenesetUpload, user: UserInternal = Security(deps.full_user), cursor: Optional[deps.Cursor] = Depends(deps.cursor), -): +) -> dict: """Upload a geneset.""" - formatted_geneset_values = db_geneset_value.format_geneset_values_for_file_insert( - geneset.gene_list - ) - return {"geneset_id": 0} \ No newline at end of file + db_geneset_value.format_geneset_values_for_file_insert(geneset.gene_list) + return {"geneset_id": 0} diff --git a/src/geneweaver/api/core/config.py b/src/geneweaver/api/core/config.py index b4e7e17..1968b69 100644 --- a/src/geneweaver/api/core/config.py +++ b/src/geneweaver/api/core/config.py @@ -1,7 +1,6 @@ """A namespace for the initialized Geneweaver API configuration.""" -from geneweaver.db.core.settings_class import Settings as DBSettings - from geneweaver.api.core.config_class import GeneweaverAPIConfig +from geneweaver.db.core.settings_class import Settings as DBSettings settings = GeneweaverAPIConfig() diff --git a/src/geneweaver/api/core/config_class.py b/src/geneweaver/api/core/config_class.py index a0193c9..e45de7c 100644 --- a/src/geneweaver/api/core/config_class.py +++ b/src/geneweaver/api/core/config_class.py @@ -1,8 +1,7 @@ """Namespace for the config class for the Geneweaver API.""" -from typing import Any, Dict, Optional, List +from typing import Any, Dict, List, Optional, Union from pydantic import BaseSettings, PostgresDsn, validator -from geneweaver.db.core.settings_class import Settings class GeneweaverAPIConfig(BaseSettings): @@ -17,7 +16,10 @@ class GeneweaverAPIConfig(BaseSettings): SQLALCHEMY_DATABASE_URI: Optional[PostgresDsn] = None @validator("SQLALCHEMY_DATABASE_URI", pre=True) - def assemble_db_connection(cls, v: Optional[str], values: Dict[str, Any]) -> Any: + def assemble_db_connection( + cls, v: Optional[str], values: Dict[str, Any] # noqa: N805 + ) -> Union[str, PostgresDsn]: + """Build the database connection string.""" if isinstance(v, str): return v return PostgresDsn.build( diff --git a/src/geneweaver/api/core/db.py b/src/geneweaver/api/core/db.py index e69de29..806d75e 100644 --- a/src/geneweaver/api/core/db.py +++ b/src/geneweaver/api/core/db.py @@ -0,0 +1 @@ +"""Root for code database functionality for the Geneweaver API.""" diff --git a/src/geneweaver/api/core/deps.py b/src/geneweaver/api/core/deps.py index 76d85a2..80736af 100644 --- a/src/geneweaver/api/core/deps.py +++ b/src/geneweaver/api/core/deps.py @@ -1,5 +1,6 @@ """A module to keep track of injectable dependencies for FastAPI endpoints. -- https://fastapi.tiangolo.com/tutorial/dependencies/ + +- https://fastapi.tiangolo.com/tutorial/dependencies/. """ from geneweaver.api.core.config import settings from geneweaver.api.core.security import Auth0 diff --git a/src/geneweaver/api/core/exceptions.py b/src/geneweaver/api/core/exceptions.py index 4ea267a..997412d 100644 --- a/src/geneweaver/api/core/exceptions.py +++ b/src/geneweaver/api/core/exceptions.py @@ -1,36 +1,18 @@ -"""""" +"""Exceptions for the GeneWeaver API.""" from fastapi import HTTPException class Auth0UnauthenticatedException(HTTPException): - def __init__(self, **kwargs): + """Exception for unauthenticated requests.""" + + def __init__(self, **kwargs) -> None: # noqa: ANN003 + """Initialize the exception.""" super().__init__(401, **kwargs) class Auth0UnauthorizedException(HTTPException): - def __init__(self, **kwargs): - super().__init__(403, **kwargs) - - -class NotAHeaderRowError(Exception): - pass - - -class InvalidBatchValueLine(Exception): - pass - + """Exception for unauthorized requests.""" -class MultiLineStringError(Exception): - pass - - -class IgnoreLineError(Exception): - pass - - -class MissingRequiredHeaderError(Exception): - pass - - -class InvalidScoreThresholdException(Exception): - pass + def __init__(self, **kwargs) -> None: # noqa: ANN003 + """Initialize the exception.""" + super().__init__(403, **kwargs) diff --git a/src/geneweaver/api/dependencies.py b/src/geneweaver/api/dependencies.py index 6051a1d..c2f68aa 100644 --- a/src/geneweaver/api/dependencies.py +++ b/src/geneweaver/api/dependencies.py @@ -1,15 +1,12 @@ """Dependency injection capabilities for the GeneWeaver API.""" from typing import Generator -from fastapi import Depends -import psycopg -from psycopg.rows import dict_row -# from psycopg_pool import ConnectionPool -# pool = ConnectionPool(conninfo, **kwargs) -from geneweaver.api.core.config import settings, db_settings -from geneweaver.db.user import user_id_from_sso_id +import psycopg +from fastapi import Depends +from geneweaver.api.core.config import db_settings, settings from geneweaver.api.core.security import Auth0, UserInternal from geneweaver.db.user import by_sso_id +from psycopg.rows import dict_row auth = Auth0( domain=settings.AUTH_DOMAIN, @@ -32,5 +29,5 @@ def full_user( cursor: Cursor = Depends(cursor), user: UserInternal = Depends(auth.get_user_strict), ) -> UserInternal: - user.id = by_sso_id(cursor, user.sso_id)[0]['usr_id'] + user.id = by_sso_id(cursor, user.sso_id)[0]["usr_id"] yield user diff --git a/src/geneweaver/api/schemas/auth.py b/src/geneweaver/api/schemas/auth.py index 57278f6..dfcfe86 100644 --- a/src/geneweaver/api/schemas/auth.py +++ b/src/geneweaver/api/schemas/auth.py @@ -1,5 +1,6 @@ +"""Authentication Related Schemas.""" from enum import Enum -from typing import Optional, List +from typing import List, Optional from pydantic import BaseModel, Field From 64be3cbbfaa6d2eaec5386effbf7b04db72895f3 Mon Sep 17 00:00:00 2001 From: Alexander Berger Date: Mon, 4 Dec 2023 11:50:48 -0500 Subject: [PATCH 05/13] Fixing formatting and lint errors in security module --- src/geneweaver/api/core/security.py | 108 +++++++++++++++++----------- 1 file changed, 68 insertions(+), 40 deletions(-) diff --git a/src/geneweaver/api/core/security.py b/src/geneweaver/api/core/security.py index 98a01a0..621d0d5 100644 --- a/src/geneweaver/api/core/security.py +++ b/src/geneweaver/api/core/security.py @@ -1,61 +1,78 @@ -"""This file contains code to authenticate a user to the API.""" -import requests +"""Code to authenticate a user to the API.""" +# ruff: noqa: B008 import urllib.parse +from typing import Dict, Optional, Type, Union -from typing import Optional, Dict, Type - -from fastapi import HTTPException, Depends, Request +import requests +from fastapi import Depends, HTTPException, Request from fastapi.logger import logger -from fastapi.security import SecurityScopes, HTTPBearer, HTTPAuthorizationCredentials -from fastapi.security import OAuth2 from fastapi.openapi.models import OAuthFlows -from pydantic import ValidationError -from jose import jwt # type: ignore - -from geneweaver.api.schemas.auth import UserInternal - +from fastapi.security import ( + HTTPAuthorizationCredentials, + HTTPBearer, + OAuth2, + SecurityScopes, +) from geneweaver.api.core.exceptions import ( Auth0UnauthenticatedException, Auth0UnauthorizedException, ) +from geneweaver.api.schemas.auth import UserInternal +from jose import jwt # type: ignore +from pydantic import ValidationError class Auth0HTTPBearer(HTTPBearer): - async def __call__(self, request: Request): - # logger.debug('Called Auth0HTTPBearer') + """Auth0 Specific HTTP Bearer Authentication.""" + + async def __call__( + self, request: Request + ) -> Optional[HTTPAuthorizationCredentials]: + """Call the HTTP Bearer __call__ method.""" return await super().__call__(request) class OAuth2ImplicitBearer(OAuth2): + """OAuth2 Implicit Flow with Bearer Token Authorization.""" + def __init__( self, - authorizationUrl: str, - scopes: Dict[str, str] = {}, + authorizationUrl: str, # noqa: N803 + scopes: Optional[Dict[str, str]] = None, scheme_name: Optional[str] = None, auto_error: bool = True, - ): + ) -> None: + """Initialize the OAuth2ImplicitBearer class.""" + scopes = {} if scopes is None else scopes flows = OAuthFlows( implicit={"authorizationUrl": authorizationUrl, "scopes": scopes} ) super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error) async def __call__(self, request: Request) -> Optional[str]: - # Overwrite parent call to prevent useless overhead, the actual auth is done in Auth0.get_user - # This scheme is just for Swagger UI + """Overwrite the __call__ method to prevent useless overhead. + + The actual auth is done in Auth0.get_user, this scheme is just for Swagger UI. + """ return None class Auth0: + """Auth0 Authentication Scheme.""" + def __init__( self, domain: str, api_audience: str, - scopes: Dict[str, str] = {}, + scopes: Union[Dict[str, str]] = None, auto_error: bool = True, scope_auto_error: bool = True, email_auto_error: bool = False, auth0user_model: Type[UserInternal] = UserInternal, - ): + ) -> None: + """Initialize the Auth0 class.""" + scopes = {} if scopes is None else scopes + self.domain = domain self.audience = api_audience @@ -83,6 +100,7 @@ async def public( Auth0HTTPBearer(auto_error=False) ), ) -> bool: + """Check if the user is public.""" return not bool(await self.get_user(security_scopes, creds)) async def authenticated( @@ -92,6 +110,7 @@ async def authenticated( Auth0HTTPBearer(auto_error=False) ), ) -> bool: + """Check if the user is authenticated.""" try: authenticated = bool(await self.get_user(security_scopes, creds)) except (Auth0UnauthorizedException, HTTPException): @@ -106,6 +125,7 @@ async def get_auth_header( ), auto_error_auth: Optional[bool] = False, ) -> Optional[Dict[str, str]]: + """Get the auth header from the token.""" user = await self.get_user(security_scopes, creds, auto_error_auth) return user.auth_header if user else None @@ -115,10 +135,11 @@ async def get_user_strict( creds: Optional[HTTPAuthorizationCredentials] = Depends( Auth0HTTPBearer(auto_error=False) ), - ): + ) -> UserInternal: + """Get the user from the token, raise an exception if not found.""" return await self.get_user(security_scopes, creds, True) - async def get_user( + async def get_user( # noqa: C901 self, security_scopes: SecurityScopes, creds: Optional[HTTPAuthorizationCredentials] = Depends( @@ -127,6 +148,7 @@ async def get_user( auto_error_auth: Optional[bool] = True, disallow_public: Optional[bool] = True, ) -> Optional[UserInternal]: + """Get the user from the token, don't error if not found.""" auto_error_auth = ( self.auto_error if auto_error_auth is None else auto_error_auth ) @@ -134,14 +156,14 @@ async def get_user( logger.debug(f"`disallow_public` is {'ON' if disallow_public else 'OFF'}") if creds is None: if disallow_public: - logger.debug(f"No credentials found, raising HTTP 403 exception") + logger.debug("No credentials found, raising HTTP 403 exception") # See HTTPBearer from FastAPI: # latest - https://github.com/tiangolo/fastapi/blob/master/fastapi/security/http.py # 0.65.1 - https://github.com/tiangolo/fastapi/blob/aece74982d7c9c1acac98e2c872c4cb885677fc7/fastapi/security/http.py # must be 403 until solving https://github.com/tiangolo/fastapi/pull/2120 raise HTTPException(403, detail="Missing bearer token") else: - logger.debug(f"No credentials found, returning None") + logger.debug("No credentials found, returning None") return None token = creds.credentials @@ -158,7 +180,6 @@ async def get_user( "n": key["n"], "e": key["e"], } - # break # TODO: do we still need to iterate all keys after we found a match? if rsa_key: payload = jwt.decode( token, @@ -172,30 +193,32 @@ async def get_user( if auto_error_auth: raise jwt.JWTError - except jwt.ExpiredSignatureError: + except jwt.ExpiredSignatureError as e: if auto_error_auth: - raise Auth0UnauthenticatedException(detail="Expired token") + raise Auth0UnauthenticatedException(detail="Expired token") from e else: return None - except jwt.JWTClaimsError: + except jwt.JWTClaimsError as e: if auto_error_auth: raise Auth0UnauthenticatedException( detail="Invalid token claims (please check issuer and audience)" - ) + ) from e else: return None - except jwt.JWTError: + except jwt.JWTError as e: if auto_error_auth: - raise Auth0UnauthenticatedException(detail="Malformed token") + raise Auth0UnauthenticatedException(detail="Malformed token") from e else: return None except Exception as e: logger.error(f'Handled exception decoding token: "{e}"') if auto_error_auth: - raise Auth0UnauthenticatedException(detail="Error decoding token") + raise Auth0UnauthenticatedException( + detail="Error decoding token" + ) from e else: return None @@ -210,11 +233,13 @@ async def get_user( raise Auth0UnauthorizedException( detail=f'Missing "{scope}" scope', headers={ - "WWW-Authenticate": f'Bearer scope="{security_scopes.scope_str}"' + "WWW-Authenticate": "Bearer scope=" + f'"{security_scopes.scope_str}"' }, ) else: - # This is an unlikely case but handle it just to be safe (perhaps auth0 will change the scope format) + # This is an unlikely case but handle it just to be safe + # (perhaps auth0 will change the scope format) raise Auth0UnauthorizedException( detail='Token "scope" field must be a string' ) @@ -225,7 +250,8 @@ async def get_user( user = self.auth0_user_model(**payload) if self.email_auto_error and not user.email: raise Auth0UnauthorizedException( - detail=f'Missing email claim (check auth0 rule "Add email to access token")' + detail="Missing email claim " + '(check auth0 rule "Add email to access token")' ) logger.info(f"Successfully found user in header token: {user}") @@ -234,18 +260,20 @@ async def get_user( except ValidationError as e: logger.error(f'Handled exception parsing Auth0User: "{e}"') if auto_error_auth: - raise Auth0UnauthorizedException(detail="Error parsing Auth0User") + raise Auth0UnauthorizedException( + detail="Error parsing Auth0User" + ) from e else: return None return None - def _process_payload(self, payload: dict): + def _process_payload(self, payload: dict) -> None: self._process_email(payload) - def _add_auth_info(self, token: str, payload: dict): + def _add_auth_info(self, token: str, payload: dict) -> None: payload["token"] = token payload["auth_header"] = {"Authorization": f"Bearer {token}"} - def _process_email(self, payload: dict): + def _process_email(self, payload: dict) -> None: payload["email"] = payload.pop(f"{self.audience}/claims/email") From 7c2ea45912f6621142261172601608e59576ec09 Mon Sep 17 00:00:00 2001 From: Alexander Berger Date: Mon, 4 Dec 2023 11:53:03 -0500 Subject: [PATCH 06/13] Fixing linting and formatting errors --- src/geneweaver/api/dependencies.py | 2 ++ src/geneweaver/api/main.py | 3 ++- src/geneweaver/api/schemas/auth.py | 10 +++++++++- src/geneweaver/api/schemas/score.py | 6 ++++++ src/geneweaver/api/services/batch.py | 8 ++------ src/geneweaver/api/services/geneset.py | 1 + src/geneweaver/api/services/io.py | 2 +- src/geneweaver/api/services/pubmeds.py | 1 + 8 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/geneweaver/api/dependencies.py b/src/geneweaver/api/dependencies.py index c2f68aa..6ddf8ed 100644 --- a/src/geneweaver/api/dependencies.py +++ b/src/geneweaver/api/dependencies.py @@ -1,4 +1,5 @@ """Dependency injection capabilities for the GeneWeaver API.""" +# ruff: noqa: B008 from typing import Generator import psycopg @@ -29,5 +30,6 @@ def full_user( cursor: Cursor = Depends(cursor), user: UserInternal = Depends(auth.get_user_strict), ) -> UserInternal: + """Get the full user object.""" user.id = by_sso_id(cursor, user.sso_id)[0]["usr_id"] yield user diff --git a/src/geneweaver/api/main.py b/src/geneweaver/api/main.py index ab746ab..0907858 100644 --- a/src/geneweaver/api/main.py +++ b/src/geneweaver/api/main.py @@ -1 +1,2 @@ -from geneweaver.api.controller.api import app +"""The main entrypoint to running the api.""" +from geneweaver.api.controller.api import app # noqa: F401 diff --git a/src/geneweaver/api/schemas/auth.py b/src/geneweaver/api/schemas/auth.py index dfcfe86..18dfffa 100644 --- a/src/geneweaver/api/schemas/auth.py +++ b/src/geneweaver/api/schemas/auth.py @@ -6,23 +6,31 @@ class AppRoles(str, Enum): + """Roles that a user can have in the GeneWeaver application.""" + user = "user" curator = "curator" admin = "admin" class User(BaseModel): + """User model.""" + email: Optional[str] name: Optional[str] sso_id: str = Field(None, alias="sub") - id: int = Field(None, alias="gw_id") + id: int = Field(None, alias="gw_id") # noqa: A003 role: Optional[AppRoles] = AppRoles.user class UserInternal(User): + """Internal User model.""" + auth_header: dict = {} token: str permissions: Optional[List[str]] class Config: + """Pydantic config.""" + allow_population_by_field_name = True diff --git a/src/geneweaver/api/schemas/score.py b/src/geneweaver/api/schemas/score.py index ffb339a..a459999 100644 --- a/src/geneweaver/api/schemas/score.py +++ b/src/geneweaver/api/schemas/score.py @@ -1,3 +1,7 @@ +"""Pydantic schemas for score types. + +NOTE: These schemas might be duplicates of schemas available in geneweaver.core. +""" from enum import Enum from typing import Optional @@ -15,6 +19,8 @@ class ScoreType(Enum): class GenesetScoreType(BaseModel): + """Pydantic schema for geneset score type.""" + score_type: ScoreType threshold_low: Optional[float] = None threshold: float = 0.05 diff --git a/src/geneweaver/api/services/batch.py b/src/geneweaver/api/services/batch.py index 7510803..705938e 100644 --- a/src/geneweaver/api/services/batch.py +++ b/src/geneweaver/api/services/batch.py @@ -1,13 +1,9 @@ """Service functions for dealing with batch files.""" -from enum import Enum from typing import List, Tuple from fastapi import UploadFile - -from geneweaver.core.parse import batch - -from geneweaver.api.schemas.batch import BatchUploadGeneset, GenesetValueInput from geneweaver.api.schemas.messages import SystemMessage, UserMessage +from geneweaver.core.parse import batch async def process_batch_file( @@ -42,7 +38,7 @@ async def process_batch_file( async def read_file_contents(batch_file: UploadFile, encoding: str = "utf-8") -> str: - """Reads the contents of an async file and decodes it using a specified encoding. + """Read the contents of an async file and decodes it using a specified encoding. This function uses an asynchronous read operation to get the contents of the batch_file, and then decodes those contents from bytes to a string using the diff --git a/src/geneweaver/api/services/geneset.py b/src/geneweaver/api/services/geneset.py index e69de29..d886464 100644 --- a/src/geneweaver/api/services/geneset.py +++ b/src/geneweaver/api/services/geneset.py @@ -0,0 +1 @@ +"""Service functions for dealing with genesets.""" diff --git a/src/geneweaver/api/services/io.py b/src/geneweaver/api/services/io.py index e3f7c55..530fe49 100644 --- a/src/geneweaver/api/services/io.py +++ b/src/geneweaver/api/services/io.py @@ -3,7 +3,7 @@ async def read_file_contents(batch_file: UploadFile, encoding: str = "utf-8") -> str: - """Reads the contents of an async file and decodes it using a specified encoding. + """Read the contents of an async file and decodes it using a specified encoding. This function uses an asynchronous read operation to get the contents of the batch_file, and then decodes those contents from bytes to a string using the diff --git a/src/geneweaver/api/services/pubmeds.py b/src/geneweaver/api/services/pubmeds.py index e69de29..5cd0bd5 100644 --- a/src/geneweaver/api/services/pubmeds.py +++ b/src/geneweaver/api/services/pubmeds.py @@ -0,0 +1 @@ +"""Service namespace for dealing with PubMed data.""" From fb23582a88a5df7d614ff892922144a54aeeabfb Mon Sep 17 00:00:00 2001 From: Alexander Berger Date: Mon, 4 Dec 2023 11:53:52 -0500 Subject: [PATCH 07/13] Adding docstrings to init py files --- src/geneweaver/api/schemas/__init__.py | 5 +++++ src/geneweaver/api/services/__init__.py | 6 ++++++ src/geneweaver/api/services/parse/__init__.py | 1 + tests/__init__.py | 1 + tests/services/__init__.py | 1 + tests/services/io/__init__.py | 1 + tests/services/parse/__init__.py | 1 + tests/services/parse/batch/__init__.py | 1 + 8 files changed, 17 insertions(+) diff --git a/src/geneweaver/api/schemas/__init__.py b/src/geneweaver/api/schemas/__init__.py index e69de29..984d2e6 100644 --- a/src/geneweaver/api/schemas/__init__.py +++ b/src/geneweaver/api/schemas/__init__.py @@ -0,0 +1,5 @@ +"""Schemas that are specific to the geneweaver API. + +Note: Most Geneweaver schemas are defined in the geneweaver-core package: +`geneweaver.core.schemas`. +""" diff --git a/src/geneweaver/api/services/__init__.py b/src/geneweaver/api/services/__init__.py index e69de29..465f546 100644 --- a/src/geneweaver/api/services/__init__.py +++ b/src/geneweaver/api/services/__init__.py @@ -0,0 +1,6 @@ +"""Services provide the functionality for the API endpoints. + +All code that is not directly related to API definitions should be placed in +this package. This includes database interactions, file IO, and other +operations that are not directly related to the API endpoints. +""" diff --git a/src/geneweaver/api/services/parse/__init__.py b/src/geneweaver/api/services/parse/__init__.py index e69de29..1159069 100644 --- a/src/geneweaver/api/services/parse/__init__.py +++ b/src/geneweaver/api/services/parse/__init__.py @@ -0,0 +1 @@ +"""Module for parsing GeneWeaver data files.""" diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..43588a7 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""The root of the tests module.""" diff --git a/tests/services/__init__.py b/tests/services/__init__.py index e69de29..a36d69c 100644 --- a/tests/services/__init__.py +++ b/tests/services/__init__.py @@ -0,0 +1 @@ +"""Tests for the API services.""" diff --git a/tests/services/io/__init__.py b/tests/services/io/__init__.py index e69de29..69b1c32 100644 --- a/tests/services/io/__init__.py +++ b/tests/services/io/__init__.py @@ -0,0 +1 @@ +"""Test io services.""" diff --git a/tests/services/parse/__init__.py b/tests/services/parse/__init__.py index e69de29..455341a 100644 --- a/tests/services/parse/__init__.py +++ b/tests/services/parse/__init__.py @@ -0,0 +1 @@ +"""Tests for the parse services.""" diff --git a/tests/services/parse/batch/__init__.py b/tests/services/parse/batch/__init__.py index e69de29..4221510 100644 --- a/tests/services/parse/batch/__init__.py +++ b/tests/services/parse/batch/__init__.py @@ -0,0 +1 @@ +"""Tests for the Batch File Parsing services.""" From 44b0668e12945652fea4ce31f92791ccc1c82a6b Mon Sep 17 00:00:00 2001 From: Alexander Berger Date: Mon, 4 Dec 2023 11:55:21 -0500 Subject: [PATCH 08/13] Cleanup of batch code and associated tests --- src/geneweaver/api/schemas/batch.py | 57 +--------------------- src/geneweaver/api/services/parse/batch.py | 16 +++--- tests/services/parse/batch/conftest.py | 5 +- tests/services/parse/batch/const.py | 1 + tests/services/parse/batch/test_parse.py | 1 + 5 files changed, 16 insertions(+), 64 deletions(-) diff --git a/src/geneweaver/api/schemas/batch.py b/src/geneweaver/api/schemas/batch.py index 7a354a9..ec5fa31 100644 --- a/src/geneweaver/api/schemas/batch.py +++ b/src/geneweaver/api/schemas/batch.py @@ -1,11 +1,8 @@ """Module for defining schemas for batch endpoints.""" -from typing import List, Optional - -from pydantic import BaseModel, validator -from geneweaver.core.parse.score import parse_score +from typing import List from geneweaver.api.schemas.messages import MessageResponse -from geneweaver.api.schemas.score import GenesetScoreType +from pydantic import BaseModel class BatchResponse(BaseModel): @@ -13,53 +10,3 @@ class BatchResponse(BaseModel): genesets: List[int] messages: MessageResponse - - -class Publication(BaseModel): - authors: str - title: str - abstract: str - journal: str - volume: str - pages: str - month: str - year: str - pubmed: str - - -class GenesetValueInput(BaseModel): - symbol: str - value: float - - -class GenesetValue(BaseModel): - ode_gene_id: str - value: float - ode_ref_id: str - threshold: bool - - -class BatchUploadGeneset(BaseModel): - score: GenesetScoreType - # TODO: Use enum from core - species: str - gene_id_type: str - pubmed_id: str - private: bool = True - curation_id: Optional[int] = None - abbreviation: str - name: str - description: str - values: List[GenesetValueInput] - - @validator("score", pre=True) - def initialize_score(cls, v): - return parse_score(v) - - @validator("private", pre=True) - def private_to_bool(cls, v): - return v.lower() != "public" - - @validator("curation_id", pre=True) - def curation_id_to_int(cls, v, values): - return 5 if values["private"] else 4 diff --git a/src/geneweaver/api/services/parse/batch.py b/src/geneweaver/api/services/parse/batch.py index ad38b3d..0a75172 100644 --- a/src/geneweaver/api/services/parse/batch.py +++ b/src/geneweaver/api/services/parse/batch.py @@ -1,13 +1,17 @@ +"""Functions for processing batch files. + +Most of the functionality for processing batch files is contained in the +geneweaver.core module. This module contains functions for reading the contents +of a file and passing those contents to the core module for processing. +""" from typing import List, Tuple -from fastapi import UploadFile +from fastapi import UploadFile +from geneweaver.api.services.io import read_file_contents from geneweaver.core.parse import batch from geneweaver.core.schema.messages import SystemMessage, UserMessage -from geneweaver.api.services.io import read_file_contents - - async def process_batch_file( # TODO: Add the database session to the function signature. # db: Session, @@ -38,10 +42,6 @@ async def process_batch_file( print(geneset, "\n") # TODO: Add the genesets to the database - # results = [ - # batch_geneset_for_user(db, user_id, geneset) - # for geneset in genesets - # ] # TODO: Return the correct values. return [10], [], [] diff --git a/tests/services/parse/batch/conftest.py b/tests/services/parse/batch/conftest.py index 4234ad0..aba8611 100644 --- a/tests/services/parse/batch/conftest.py +++ b/tests/services/parse/batch/conftest.py @@ -1,14 +1,16 @@ +"""Pytest fixtures for the batch parsing service tests.""" from unittest.mock import AsyncMock import pytest from fastapi import UploadFile -from tests.api.unit.services.parse.batch import const +from tests.services.parse.batch import const # Create a pytest fixture for the mocked UploadFile @pytest.fixture() def mock_upload_file(): + """Provide a mocked UploadFile object.""" mock_file = AsyncMock(spec=UploadFile) return mock_file # provide the mock object to the test @@ -23,4 +25,5 @@ def mock_upload_file(): ] ) def example_batch_file_contents(request) -> str: + """Provide the contents of the example batch file.""" return request.param diff --git a/tests/services/parse/batch/const.py b/tests/services/parse/batch/const.py index 54684a0..0e5319d 100644 --- a/tests/services/parse/batch/const.py +++ b/tests/services/parse/batch/const.py @@ -1,3 +1,4 @@ +"""Constants for batch parsing tests.""" # ruff: noqa: E501 EXAMPLE_BATCH_FILE = """ # This is an example batch upload file for GeneWeaver. diff --git a/tests/services/parse/batch/test_parse.py b/tests/services/parse/batch/test_parse.py index e69de29..b5f61fd 100644 --- a/tests/services/parse/batch/test_parse.py +++ b/tests/services/parse/batch/test_parse.py @@ -0,0 +1 @@ +"""Test for the parsing service.""" From e748c83c0df73cd79b4a383483bd5f934692f37e Mon Sep 17 00:00:00 2001 From: Alexander Berger Date: Mon, 4 Dec 2023 11:55:48 -0500 Subject: [PATCH 09/13] Adding missing docstring --- tests/services/io/test_read_file_contents.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/services/io/test_read_file_contents.py b/tests/services/io/test_read_file_contents.py index e5a6042..a9e5068 100644 --- a/tests/services/io/test_read_file_contents.py +++ b/tests/services/io/test_read_file_contents.py @@ -72,6 +72,7 @@ ], ) async def test_read_file_contents(contents, encoding, expected, mock_upload_file): + """Test the read_file_contents function.""" # Set up the mock to return the test contents when read mock_upload_file.read.return_value = contents From 1a3892c571df8bed01d13eaf1ea19d4218357fbf Mon Sep 17 00:00:00 2001 From: Alexander Berger Date: Tue, 5 Dec 2023 12:55:42 -0500 Subject: [PATCH 10/13] Fixing test fixture location --- tests/services/{parse/batch => }/conftest.py | 0 tests/services/{parse/batch => }/const.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/services/{parse/batch => }/conftest.py (100%) rename tests/services/{parse/batch => }/const.py (100%) diff --git a/tests/services/parse/batch/conftest.py b/tests/services/conftest.py similarity index 100% rename from tests/services/parse/batch/conftest.py rename to tests/services/conftest.py diff --git a/tests/services/parse/batch/const.py b/tests/services/const.py similarity index 100% rename from tests/services/parse/batch/const.py rename to tests/services/const.py From 0898a2f20bc975e8fa04d1ce2d22825b85cc7450 Mon Sep 17 00:00:00 2001 From: Alexander Berger Date: Tue, 5 Dec 2023 12:56:03 -0500 Subject: [PATCH 11/13] Fixing import path for test cost --- tests/services/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/services/conftest.py b/tests/services/conftest.py index aba8611..d65df3e 100644 --- a/tests/services/conftest.py +++ b/tests/services/conftest.py @@ -4,7 +4,7 @@ import pytest from fastapi import UploadFile -from tests.services.parse.batch import const +from tests.services import const # Create a pytest fixture for the mocked UploadFile From 30405ceb624160ee885c8d677489dac522855211 Mon Sep 17 00:00:00 2001 From: Alexander Berger Date: Tue, 5 Dec 2023 12:56:46 -0500 Subject: [PATCH 12/13] Updating dependencies and lock file --- poetry.lock | 306 +++++++++++++++++++++++++++++++++++-------------- pyproject.toml | 22 ++-- 2 files changed, 233 insertions(+), 95 deletions(-) diff --git a/poetry.lock b/poetry.lock index d994d0b..3679a7d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -491,43 +491,45 @@ all = ["email-validator (>=1.1.1)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)" [[package]] name = "geneweaver-core" -version = "0.2.0a0" +version = "0.8.0a0" description = "The core of the Jax-Geneweaver Python library" optional = false python-versions = ">=3.9,<4.0" files = [ - {file = "geneweaver_core-0.2.0a0-py3-none-any.whl", hash = "sha256:71b6cfd2633184541f183a19eeb2aa20add4e11c850fcb956e8fe778418cdf67"}, - {file = "geneweaver_core-0.2.0a0.tar.gz", hash = "sha256:71d829ba3823feace06c9cf4a548b5761e1409718443feb20b3eb7311ed06824"}, + {file = "geneweaver_core-0.8.0a0-py3-none-any.whl", hash = "sha256:01a70f8dc9407def33a706cced4e4946de66b10f61e828d69d2b686b39a5a197"}, + {file = "geneweaver_core-0.8.0a0.tar.gz", hash = "sha256:b4fc0373f3ac21914278a9d9961942deef2130e230ac336d5414591db8f25f2c"}, ] [package.dependencies] +numpy = ">=1.22,<1.24" openpyxl = ">=3.1.2,<4.0.0" +pandas = ">=1.5,<2.1" pydantic = {version = ">=1.10.7,<2.0.0", extras = ["dotenv"]} [[package]] name = "geneweaver-db" -version = "0.1.6a0" +version = "0.2.0a0" description = "Database Interaction Services for GeneWeaver" optional = false python-versions = ">=3.9,<4.0" files = [ - {file = "geneweaver_db-0.1.6a0-py3-none-any.whl", hash = "sha256:43afdb19bb46c750d668d542b86b4236ce978cdb0318bb3ee22128204c099ee7"}, - {file = "geneweaver_db-0.1.6a0.tar.gz", hash = "sha256:103e3a95ea65c693ee16f210b61b29bc214e1681fb00f20c4543b35888f0c5a2"}, + {file = "geneweaver_db-0.2.0a0-py3-none-any.whl", hash = "sha256:9d13bac879d68d3e471e1f795b9f0f71602f61335a1392c60bae33b150f42e6e"}, + {file = "geneweaver_db-0.2.0a0.tar.gz", hash = "sha256:a595260738bbf85a5d880f9b4c57032b98c5369ecd10a9623524b1b2f737c01e"}, ] [package.dependencies] -geneweaver-core = ">=0.2.0a0,<0.3.0" -psycopg = {version = ">=3.1.9,<4.0.0", extras = ["binary"]} +geneweaver-core = ">=0.8.0a0,<0.9.0" +psycopg = {version = ">=3.1.13,<4.0.0", extras = ["binary"]} [[package]] name = "geneweaver-testing" -version = "0.0.1b0" +version = "0.0.3" description = "A library to standardize testing of GeneWeaver pacakges." optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "geneweaver_testing-0.0.1b0-py3-none-any.whl", hash = "sha256:29bf5cb790fc5f788eb07bdd4df70a9edbeb9b84f467bce4b6e537b1cada3d47"}, - {file = "geneweaver_testing-0.0.1b0.tar.gz", hash = "sha256:550b1490a3404357bbaf8dbdb361e3c70efe76f76966b8b03cdcc747c3e8e5e3"}, + {file = "geneweaver_testing-0.0.3-py3-none-any.whl", hash = "sha256:dd9d04cd9ff6366496a9f66ea3347121d0d810d9748936db736837d6f773d7f5"}, + {file = "geneweaver_testing-0.0.3.tar.gz", hash = "sha256:e268462ced2d44ad213cae7cb2e669d5c0065e6bf82965730b7d3eb0f0f0e9a8"}, ] [package.dependencies] @@ -824,6 +826,43 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "numpy" +version = "1.23.5" +description = "NumPy is the fundamental package for array computing with Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "numpy-1.23.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9c88793f78fca17da0145455f0d7826bcb9f37da4764af27ac945488116efe63"}, + {file = "numpy-1.23.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e9f4c4e51567b616be64e05d517c79a8a22f3606499941d97bb76f2ca59f982d"}, + {file = "numpy-1.23.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7903ba8ab592b82014713c491f6c5d3a1cde5b4a3bf116404e08f5b52f6daf43"}, + {file = "numpy-1.23.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e05b1c973a9f858c74367553e236f287e749465f773328c8ef31abe18f691e1"}, + {file = "numpy-1.23.5-cp310-cp310-win32.whl", hash = "sha256:522e26bbf6377e4d76403826ed689c295b0b238f46c28a7251ab94716da0b280"}, + {file = "numpy-1.23.5-cp310-cp310-win_amd64.whl", hash = "sha256:dbee87b469018961d1ad79b1a5d50c0ae850000b639bcb1b694e9981083243b6"}, + {file = "numpy-1.23.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ce571367b6dfe60af04e04a1834ca2dc5f46004ac1cc756fb95319f64c095a96"}, + {file = "numpy-1.23.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56e454c7833e94ec9769fa0f86e6ff8e42ee38ce0ce1fa4cbb747ea7e06d56aa"}, + {file = "numpy-1.23.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5039f55555e1eab31124a5768898c9e22c25a65c1e0037f4d7c495a45778c9f2"}, + {file = "numpy-1.23.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f545efd1108e647604a1b5aa809591ccd2540f468a880bedb97247e72db387"}, + {file = "numpy-1.23.5-cp311-cp311-win32.whl", hash = "sha256:b2a9ab7c279c91974f756c84c365a669a887efa287365a8e2c418f8b3ba73fb0"}, + {file = "numpy-1.23.5-cp311-cp311-win_amd64.whl", hash = "sha256:0cbe9848fad08baf71de1a39e12d1b6310f1d5b2d0ea4de051058e6e1076852d"}, + {file = "numpy-1.23.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f063b69b090c9d918f9df0a12116029e274daf0181df392839661c4c7ec9018a"}, + {file = "numpy-1.23.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0aaee12d8883552fadfc41e96b4c82ee7d794949e2a7c3b3a7201e968c7ecab9"}, + {file = "numpy-1.23.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92c8c1e89a1f5028a4c6d9e3ccbe311b6ba53694811269b992c0b224269e2398"}, + {file = "numpy-1.23.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d208a0f8729f3fb790ed18a003f3a57895b989b40ea4dce4717e9cf4af62c6bb"}, + {file = "numpy-1.23.5-cp38-cp38-win32.whl", hash = "sha256:06005a2ef6014e9956c09ba07654f9837d9e26696a0470e42beedadb78c11b07"}, + {file = "numpy-1.23.5-cp38-cp38-win_amd64.whl", hash = "sha256:ca51fcfcc5f9354c45f400059e88bc09215fb71a48d3768fb80e357f3b457e1e"}, + {file = "numpy-1.23.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8969bfd28e85c81f3f94eb4a66bc2cf1dbdc5c18efc320af34bffc54d6b1e38f"}, + {file = "numpy-1.23.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7ac231a08bb37f852849bbb387a20a57574a97cfc7b6cabb488a4fc8be176de"}, + {file = "numpy-1.23.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf837dc63ba5c06dc8797c398db1e223a466c7ece27a1f7b5232ba3466aafe3d"}, + {file = "numpy-1.23.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33161613d2269025873025b33e879825ec7b1d831317e68f4f2f0f84ed14c719"}, + {file = "numpy-1.23.5-cp39-cp39-win32.whl", hash = "sha256:af1da88f6bc3d2338ebbf0e22fe487821ea4d8e89053e25fa59d1d79786e7481"}, + {file = "numpy-1.23.5-cp39-cp39-win_amd64.whl", hash = "sha256:09b7847f7e83ca37c6e627682f145856de331049013853f344f37b0c9690e3df"}, + {file = "numpy-1.23.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:abdde9f795cf292fb9651ed48185503a2ff29be87770c3b8e2a14b0cd7aa16f8"}, + {file = "numpy-1.23.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9a909a8bae284d46bbfdefbdd4a262ba19d3bc9921b1e76126b1d21c3c34135"}, + {file = "numpy-1.23.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:01dd17cbb340bf0fc23981e52e1d18a9d4050792e8fb8363cecbf066a84b827d"}, + {file = "numpy-1.23.5.tar.gz", hash = "sha256:1b1766d6f397c18153d40015ddfc79ddb715cabadc04d2d228d4e5a8bc4ded1a"}, +] + [[package]] name = "openpyxl" version = "3.1.2" @@ -853,7 +892,6 @@ files = [ {file = "orjson-3.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a39c2529d75373b7167bf84c814ef9b8f3737a339c225ed6c0df40736df8748"}, {file = "orjson-3.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:84ebd6fdf138eb0eb4280045442331ee71c0aab5e16397ba6645f32f911bfb37"}, {file = "orjson-3.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a60a1cfcfe310547a1946506dd4f1ed0a7d5bd5b02c8697d9d5dcd8d2e9245e"}, - {file = "orjson-3.9.2-cp310-none-win32.whl", hash = "sha256:2ae61f5d544030a6379dbc23405df66fea0777c48a0216d2d83d3e08b69eb676"}, {file = "orjson-3.9.2-cp310-none-win_amd64.whl", hash = "sha256:c290c4f81e8fd0c1683638802c11610b2f722b540f8e5e858b6914b495cf90c8"}, {file = "orjson-3.9.2-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:02ef014f9a605e84b675060785e37ec9c0d2347a04f1307a9d6840ab8ecd6f55"}, {file = "orjson-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:992af54265ada1c1579500d6594ed73fe333e726de70d64919cf37f93defdd06"}, @@ -863,7 +901,6 @@ files = [ {file = "orjson-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275b5a18fd9ed60b2720543d3ddac170051c43d680e47d04ff5203d2c6d8ebf1"}, {file = "orjson-3.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b9aea6dcb99fcbc9f6d1dd84fca92322fda261da7fb014514bb4689c7c2097a8"}, {file = "orjson-3.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7d74ae0e101d17c22ef67b741ba356ab896fc0fa64b301c2bf2bb0a4d874b190"}, - {file = "orjson-3.9.2-cp311-none-win32.whl", hash = "sha256:a9a7d618f99b2d67365f2b3a588686195cb6e16666cd5471da603a01315c17cc"}, {file = "orjson-3.9.2-cp311-none-win_amd64.whl", hash = "sha256:6320b28e7bdb58c3a3a5efffe04b9edad3318d82409e84670a9b24e8035a249d"}, {file = "orjson-3.9.2-cp37-cp37m-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:368e9cc91ecb7ac21f2aa475e1901204110cf3e714e98649c2502227d248f947"}, {file = "orjson-3.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58e9e70f0dcd6a802c35887f306b555ff7a214840aad7de24901fc8bd9cf5dde"}, @@ -873,7 +910,6 @@ files = [ {file = "orjson-3.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e46e9c5b404bb9e41d5555762fd410d5466b7eb1ec170ad1b1609cbebe71df21"}, {file = "orjson-3.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8170157288714678ffd64f5de33039e1164a73fd8b6be40a8a273f80093f5c4f"}, {file = "orjson-3.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e3e2f087161947dafe8319ea2cfcb9cea4bb9d2172ecc60ac3c9738f72ef2909"}, - {file = "orjson-3.9.2-cp37-none-win32.whl", hash = "sha256:373b7b2ad11975d143556fdbd2c27e1150b535d2c07e0b48dc434211ce557fe6"}, {file = "orjson-3.9.2-cp37-none-win_amd64.whl", hash = "sha256:d7de3dbbe74109ae598692113cec327fd30c5a30ebca819b21dfa4052f7b08ef"}, {file = "orjson-3.9.2-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8cd4385c59bbc1433cad4a80aca65d2d9039646a9c57f8084897549b55913b17"}, {file = "orjson-3.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a74036aab1a80c361039290cdbc51aa7adc7ea13f56e5ef94e9be536abd227bd"}, @@ -883,7 +919,6 @@ files = [ {file = "orjson-3.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1882a70bb69595b9ec5aac0040a819e94d2833fe54901e2b32f5e734bc259a8b"}, {file = "orjson-3.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fc05e060d452145ab3c0b5420769e7356050ea311fc03cb9d79c481982917cca"}, {file = "orjson-3.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f8bc2c40d9bb26efefb10949d261a47ca196772c308babc538dd9f4b73e8d386"}, - {file = "orjson-3.9.2-cp38-none-win32.whl", hash = "sha256:302d80198d8d5b658065627da3a356cbe5efa082b89b303f162f030c622e0a17"}, {file = "orjson-3.9.2-cp38-none-win_amd64.whl", hash = "sha256:3164fc20a585ec30a9aff33ad5de3b20ce85702b2b2a456852c413e3f0d7ab09"}, {file = "orjson-3.9.2-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7a6ccadf788531595ed4728aa746bc271955448d2460ff0ef8e21eb3f2a281ba"}, {file = "orjson-3.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3245d230370f571c945f69aab823c279a868dc877352817e22e551de155cb06c"}, @@ -893,7 +928,6 @@ files = [ {file = "orjson-3.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03fb36f187a0c19ff38f6289418863df8b9b7880cdbe279e920bef3a09d8dab1"}, {file = "orjson-3.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:20925d07a97c49c6305bff1635318d9fc1804aa4ccacb5fb0deb8a910e57d97a"}, {file = "orjson-3.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:eebfed53bec5674e981ebe8ed2cf00b3f7bcda62d634733ff779c264307ea505"}, - {file = "orjson-3.9.2-cp39-none-win32.whl", hash = "sha256:ba60f09d735f16593950c6adf033fbb526faa94d776925579a87b777db7d0838"}, {file = "orjson-3.9.2-cp39-none-win_amd64.whl", hash = "sha256:869b961df5fcedf6c79f4096119b35679b63272362e9b745e668f0391a892d39"}, {file = "orjson-3.9.2.tar.gz", hash = "sha256:24257c8f641979bf25ecd3e27251b5cc194cdd3a6e96004aac8446f5e63d9664"}, ] @@ -909,6 +943,73 @@ files = [ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] +[[package]] +name = "pandas" +version = "2.0.3" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pandas-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8"}, + {file = "pandas-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce0c6f76a0f1ba361551f3e6dceaff06bde7514a374aa43e33b588ec10420183"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba619e410a21d8c387a1ea6e8a0e49bb42216474436245718d7f2e88a2f8d7c0"}, + {file = "pandas-2.0.3-cp310-cp310-win32.whl", hash = "sha256:3ef285093b4fe5058eefd756100a367f27029913760773c8bf1d2d8bebe5d210"}, + {file = "pandas-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:9ee1a69328d5c36c98d8e74db06f4ad518a1840e8ccb94a4ba86920986bb617e"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b084b91d8d66ab19f5bb3256cbd5ea661848338301940e17f4492b2ce0801fe8"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37673e3bdf1551b95bf5d4ce372b37770f9529743d2498032439371fc7b7eb26"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9cb1e14fdb546396b7e1b923ffaeeac24e4cedd14266c3497216dd4448e4f2d"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9cd88488cceb7635aebb84809d087468eb33551097d600c6dad13602029c2df"}, + {file = "pandas-2.0.3-cp311-cp311-win32.whl", hash = "sha256:694888a81198786f0e164ee3a581df7d505024fbb1f15202fc7db88a71d84ebd"}, + {file = "pandas-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6a21ab5c89dcbd57f78d0ae16630b090eec626360085a4148693def5452d8a6b"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4da0d45e7f34c069fe4d522359df7d23badf83abc1d1cef398895822d11061"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32fca2ee1b0d93dd71d979726b12b61faa06aeb93cf77468776287f41ff8fdc5"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d3624b3ae734490e4d63c430256e716f488c4fcb7c8e9bde2d3aa46c29089"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eae3dc34fa1aa7772dd3fc60270d13ced7346fcbcfee017d3132ec625e23bb0"}, + {file = "pandas-2.0.3-cp38-cp38-win32.whl", hash = "sha256:f3421a7afb1a43f7e38e82e844e2bca9a6d793d66c1a7f9f0ff39a795bbc5e02"}, + {file = "pandas-2.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:69d7f3884c95da3a31ef82b7618af5710dba95bb885ffab339aad925c3e8ce78"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5247fb1ba347c1261cbbf0fcfba4a3121fbb4029d95d9ef4dc45406620b25c8b"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81af086f4543c9d8bb128328b5d32e9986e0c84d3ee673a2ac6fb57fd14f755e"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1994c789bf12a7c5098277fb43836ce090f1073858c10f9220998ac74f37c69b"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ec591c48e29226bcbb316e0c1e9423622bc7a4eaf1ef7c3c9fa1a3981f89641"}, + {file = "pandas-2.0.3-cp39-cp39-win32.whl", hash = "sha256:04dbdbaf2e4d46ca8da896e1805bc04eb85caa9a82e259e8eed00254d5e0c682"}, + {file = "pandas-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:1168574b036cd8b93abc746171c9b4f1b83467438a5e45909fed645cf8692dbc"}, + {file = "pandas-2.0.3.tar.gz", hash = "sha256:c02f372a88e0d17f36d3093a644c73cfc1788e876a7c4bcb4020a77512e2043c"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.20.3", markers = "python_version < \"3.10\""}, + {version = ">=1.21.0", markers = "python_version >= \"3.10\""}, + {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.1" + +[package.extras] +all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"] +aws = ["s3fs (>=2021.08.0)"] +clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"] +compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"] +computation = ["scipy (>=1.7.1)", "xarray (>=0.21.0)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pyxlsb (>=1.0.8)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)"] +feather = ["pyarrow (>=7.0.0)"] +fss = ["fsspec (>=2021.07.0)"] +gcp = ["gcsfs (>=2021.07.0)", "pandas-gbq (>=0.15.0)"] +hdf5 = ["tables (>=3.6.1)"] +html = ["beautifulsoup4 (>=4.9.3)", "html5lib (>=1.1)", "lxml (>=4.6.3)"] +mysql = ["SQLAlchemy (>=1.4.16)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.0.0)", "tabulate (>=0.8.9)"] +parquet = ["pyarrow (>=7.0.0)"] +performance = ["bottleneck (>=1.3.2)", "numba (>=0.53.1)", "numexpr (>=2.7.1)"] +plot = ["matplotlib (>=3.6.1)"] +postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"] +spss = ["pyreadstat (>=1.1.2)"] +sql-other = ["SQLAlchemy (>=1.4.16)"] +test = ["hypothesis (>=6.34.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.6.3)"] + [[package]] name = "pathspec" version = "0.11.1" @@ -952,89 +1053,100 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "psycopg" -version = "3.1.9" +version = "3.1.14" description = "PostgreSQL database adapter for Python" optional = false python-versions = ">=3.7" files = [ - {file = "psycopg-3.1.9-py3-none-any.whl", hash = "sha256:fbbac339274d8733ee70ba9822297af3e8871790a26e967b5ea53e30a4b74dcc"}, - {file = "psycopg-3.1.9.tar.gz", hash = "sha256:ab400f207a8c120bafdd8077916d8f6c0106e809401378708485b016508c30c9"}, + {file = "psycopg-3.1.14-py3-none-any.whl", hash = "sha256:f5bce37d357578b230ede15fb461e2c601122986f6dd590e94283aaca8958b14"}, + {file = "psycopg-3.1.14.tar.gz", hash = "sha256:7a63249f52e9c312d2d3978df5f170d21a0defd3a0c950d7859d226b7cfbfad5"}, ] [package.dependencies] -psycopg-binary = {version = "3.1.9", optional = true, markers = "extra == \"binary\""} +psycopg-binary = {version = "3.1.14", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""} typing-extensions = ">=4.1" tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] -binary = ["psycopg-binary (==3.1.9)"] -c = ["psycopg-c (==3.1.9)"] -dev = ["black (>=23.1.0)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.2)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] +binary = ["psycopg-binary (==3.1.14)"] +c = ["psycopg-c (==3.1.14)"] +dev = ["black (>=23.1.0)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.4.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] pool = ["psycopg-pool"] -test = ["anyio (>=3.6.2)", "mypy (>=1.2)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] +test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] [[package]] name = "psycopg-binary" -version = "3.1.9" +version = "3.1.14" description = "PostgreSQL database adapter for Python -- C optimisation distribution" optional = false python-versions = ">=3.7" files = [ - {file = "psycopg_binary-3.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:284038cbe3f5a0f3de417af9b5eaa2a9524a3a06211523cf245111c71b566506"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2cea4bb0b19245c83486868d7c66f73238c4caa266b5b3c3d664d10dab2ab56"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe5c5c31f59ccb1d1f473466baa93d800138186286e80e251f930e49c80d208"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82704a899d57c29beba5399d41eab5ef5c238b810d7e25e2d1916d2b34c4b1a3"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eab449e39db1c429cac79b7aa27e6827aad4995f32137e922db7254f43fed7b5"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87e0c97733b11eeca3d24e56df70f3f9d792b2abd46f48be2fb2348ffc3e7e39"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:81e34d6df54329424944d5ca91b1cc77df6b8a9130cb5480680d56f53d4e485c"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e2f463079d99568a343ed0b766150b30627e9ed41de99fd82e945e7e2bec764a"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f2cbdef6568da21c39dfd45c2074e85eabbd00e1b721832ba94980f01f582dd4"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53afb0cc2ebe74651f339e22d05ec082a0f44939715d9138d357852f074fcf55"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-win_amd64.whl", hash = "sha256:09167f106e7685591b4cdf58eff0191fb7435d586f384133a0dd30df646cf409"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8aaa47c1791fc05c0229ec1003dd49e13238fba9434e1fc3b879632f749c3c4"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d91ee0d33ac7b42d0488a9be2516efa2ec00901b81d69566ff34a7a94b66c0b"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5e36504373e5bcdc954b1da1c6fe66379007fe1e329790e8fb72b879a01e097"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c1def6c2d28e257325b3b208cf1966343b498282a0f4d390fda7b7e0577da64"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:055537a9c20efe9bf17cb72bd879602eda71de6f737ebafa1953e017c6a37fbe"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b164355d023a91b23dcc4bb3112bc7d6e9b9c938fb5abcb6e54457d2da1f317"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03b08545ce1c627f4d5e6384eda2946660c4ba6ceb0a09ae47de07419f725669"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1e31bac3d2d41e6446b20b591f638943328c958f4d1ce13d6f1c5db97c3a8dee"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:a274c63c8fb9d419509bed2ef72befc1fd04243972e17e7f5afc5725cb13a560"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:98d9d156b9ada08c271a79662fc5fcc1731b4d7c1f651ef5843d818d35f15ba0"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-win_amd64.whl", hash = "sha256:c3a13aa022853891cadbc7256a9804e5989def760115c82334bddf0d19783b0b"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1a321ef3579a8de0545ade6ff1edfde0c88b8847d58c5615c03751c76054796"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5833bda4c14f24c6a8ac08d3c5712acaa4f35aab31f9ccd2265e9e9a7d0151c8"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a207d5a7f4212443b7452851c9ccd88df9c6d4d58fa2cea2ead4dd9cb328e578"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:07414daa86662f7657e9fabe49af85a32a975e92e6568337887d9c9ffedc224f"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17c5d4936c746f5125c6ef9eb43655e27d4d0c9ffe34c3073878b43c3192511d"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5cdc13c8ec1437240801e43d07e27ff6479ac9dd8583ecf647345bfd2e8390e4"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3836bdaf030a5648bd5f5b452e4b068b265e28f9199060c5b70dbf4a218cde6e"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:96725d9691a84a21eb3e81c884a2e043054e33e176801a57a05e9ac38d142c6e"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dade344aa90bb0b57d1cfc13304ed83ab9a36614b8ddd671381b2de72fe1483d"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-win_amd64.whl", hash = "sha256:db866cc557d9761036771d666d17fa4176c537af7e6098f42a6bf8f64217935f"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3b62545cc64dd69ea0ae5ffe18d7c97e03660ab8244aa8c5172668a21c41daa0"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:058ab0d79be0b229338f0e61fec6f475077518cba63c22c593645a69f01c3e23"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2340ca2531f69e5ebd9d18987362ba57ed6ab6a271511d8026814a46a2a87b59"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b816ce0e27a2a8786d34b61d3e36e01029245025879d64b88554326b794a4f0"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b36fe4314a784fbe45c9fd71c902b9bf57341aff9b97c0cbd22f8409a271e2f"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b246fed629482b06f938b23e9281c4af592329daa3ec2cd4a6841ccbfdeb4d68"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:90787ac05b932c0fc678cbf470ccea9c385b8077583f0490136b4569ed3fb652"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9c114f678e8f4a96530fa79cfd84f65f26358ecfc6cca70cfa2d5e3ae5ef217a"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3a82e77400d1ef6c5bbcf3e600e8bdfacf1a554512f96c090c43ceca3d1ce3b6"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7d990f14a37345ca05a5192cd5ac938c9cbedca9c929872af6ae311158feb0e"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-win_amd64.whl", hash = "sha256:e0ca74fd85718723bb9f08e0c6898e901a0c365aef20b3c3a4ef8709125d6210"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ce8f4dea5934aa6c4933e559c74bef4beb3413f51fbcf17f306ce890216ac33a"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f41a9e0de4db194c053bcc7c00c35422a4d19d92a8187e8065b1c560626efe35"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f94a7985135e084e122b143956c6f589d17aef743ecd0a434a3d3a222631d5a"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bb86d58b90faefdc0bbedf08fdea4cc2afcb1cfa4340f027d458bfd01d8b812"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c696dc84f9ff155761df15779181d8e4af7746b98908e130add8259912e4bb7"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4213953da44324850c8f789301cf665f46fb94301ba403301e7af58546c3a428"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:25e3ce947aaaa1bd9f1920fca76d7281660646304f9ea5bc036b201dd8790655"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9c75be2a9b986139e3ff6bc0a2852081ac00811040f9b82d3aa539821311122e"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:63e8d1dbe253657c70dbfa9c59423f4654d82698fc5ed6868b8dc0765abe20b6"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f4da4ca9b2365fc1d3fc741c3bbd3efccd892ce813444b884c8911a1acf1c932"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-win_amd64.whl", hash = "sha256:c0b8d6bbeff1dba760a208d8bc205a05b745e6cee02b839f969f72cf56a8b80d"}, + {file = "psycopg_binary-3.1.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cf6ff7f025643e42d27d903e8be5d171db7db4f486076a6942cbf8daa4f8b3e"}, + {file = "psycopg_binary-3.1.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:57841e59f181e546c28d73036847f42eeab3e917a1cceb4a27e54b4a1d75683e"}, + {file = "psycopg_binary-3.1.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67cc4b0b3585bbce8782c7ad4a498549f8150fa77872c3cffdf476f04bbcb714"}, + {file = "psycopg_binary-3.1.14-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdb9c2e796d029eaa53881ed3de065628639fa0550fa462f145e54e4f90a429e"}, + {file = "psycopg_binary-3.1.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7da7f174a34fd0441caf2a44a71d20800f19a9565753e3531197914a7441b42b"}, + {file = "psycopg_binary-3.1.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b95c1d1be1a243e28583a70f257e970941c5b2c7f0d21e24dfc92145ad82407"}, + {file = "psycopg_binary-3.1.14-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e31bb31fa6abe74041715bc15e23e4d784887b388952503f6fa54d38bbcc3258"}, + {file = "psycopg_binary-3.1.14-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:eb1e5df4adc6514a78643ac7119de7725f089338c8907a2b40f919abe5595ce7"}, + {file = "psycopg_binary-3.1.14-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:15b3b04d8ec665c660268169364f4befab490231224486eb4c761d5365a01108"}, + {file = "psycopg_binary-3.1.14-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96c123bebc7a6d83e90f480ca44696ac804d74e4b7920b5d6ca7479ea179732e"}, + {file = "psycopg_binary-3.1.14-cp310-cp310-win_amd64.whl", hash = "sha256:2ccfa9d7e5272a6cc25038b6e2bc67a9a118765fba4cebcc8318ac0336f2ac86"}, + {file = "psycopg_binary-3.1.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eb093578a56e3074c9573d8c1bea408ae857e3e8330ed831c4d7fc5ece6f9cad"}, + {file = "psycopg_binary-3.1.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d431c41321168035dfd7217253ca02f985769636500ae951fa72cb9f6473d393"}, + {file = "psycopg_binary-3.1.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d4de5646460ea7f26fe19feffc9853648a05ee419d2f861029a1c9c53817d"}, + {file = "psycopg_binary-3.1.14-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dad6eef513754219a696f8bd51c7c5f25f6b988ac5b6edc87e698c737158f4d3"}, + {file = "psycopg_binary-3.1.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e0a41b2a2e78a785e7b415e495ae7f35817fac7377d34192b56ce7dd914c1f"}, + {file = "psycopg_binary-3.1.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33a76e665826ebb4298fb931985f466e3cd553bb7cf30cfef9214e2c9156f026"}, + {file = "psycopg_binary-3.1.14-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab1f40717be4bb12e43c6789ba05efdffd342c8c7aa9db346110d814779a8088"}, + {file = "psycopg_binary-3.1.14-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:664cff756a8a636f9c6810d41dc5da46879142865ebcceae2edcd045dcb30b6f"}, + {file = "psycopg_binary-3.1.14-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:6babfd78e6e0a91eff45b82b2517e82bfe078de084f354853b1017cc04e37ad9"}, + {file = "psycopg_binary-3.1.14-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d88c15597732b6021e29cf5bdaa7245af51a986f2834ca2c77aca4c05f224f59"}, + {file = "psycopg_binary-3.1.14-cp311-cp311-win_amd64.whl", hash = "sha256:32f09adce74208aea1b6500ab07bda68bed45df902f0125a3b292137a2133a2a"}, + {file = "psycopg_binary-3.1.14-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d5ca34fad0558244d5fbfbc19a691b423a471e9e601d4ddabd759aa05ded0fa1"}, + {file = "psycopg_binary-3.1.14-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a19d4776ccb4c22192ffa5878c82389a0d2ba8a861933b95f91b73d426b2f54f"}, + {file = "psycopg_binary-3.1.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:749760b5d963390859397b745c797b62ee0757c72a961ee77f918fd4a1ae6646"}, + {file = "psycopg_binary-3.1.14-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56e668a6a61f33d48b1696207393e5ec33a371a3692cc434c445b909f97a5e34"}, + {file = "psycopg_binary-3.1.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:abeb8ed0fe3ff635f10914edb8c8febe99046a99143d21e004352aebc4ab1241"}, + {file = "psycopg_binary-3.1.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f623d0952bc0e118c4de529a20fba98855654ff3a1f30d9bce77898af0518e70"}, + {file = "psycopg_binary-3.1.14-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:57a4055b63a824589f9e9a8edc5dcf498ef49b888bc945d7a5e865ffe5a42ff8"}, + {file = "psycopg_binary-3.1.14-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:068d29ebcba782b815a5a3842c740af8d4c314e2caf82636a5e0e80120606655"}, + {file = "psycopg_binary-3.1.14-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b1528ea7ef8f5e109d9eda470e7a7917b467938c1371245eddad0caff8edb5a7"}, + {file = "psycopg_binary-3.1.14-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fd078bb91b5957913cebefbaa8b9e296e31c4f3eaab16b875e2f75a84640ebc6"}, + {file = "psycopg_binary-3.1.14-cp312-cp312-win_amd64.whl", hash = "sha256:313be7a062e96dbc153ca7ecd6bce86fc67ac5fd9f5722cfbc5fba5431f4eb36"}, + {file = "psycopg_binary-3.1.14-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:099123c231a38dc071c9d41b983300196e05ed724b7642bc4bc9ee64ee46c5ff"}, + {file = "psycopg_binary-3.1.14-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:501a73961115c962d22ece4a0e1d8d8711cea6d6a8a2284521876973275fb01a"}, + {file = "psycopg_binary-3.1.14-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c74c27acedbc463007647ca3082b1848c07ec9c03bfaea068ee0d87050fdb629"}, + {file = "psycopg_binary-3.1.14-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8bd8d92d8418fed8c749134469676ce08b492fcaad4cfe2cde713bd33decfa7"}, + {file = "psycopg_binary-3.1.14-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39d7bccddac038d1627425b2ac9e8f054909ef8c4d0f8bfb684831dcee275ee2"}, + {file = "psycopg_binary-3.1.14-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a394ebcf129e2344d091c04254bb289b7454e913ca921570af8f2185bf8fd86"}, + {file = "psycopg_binary-3.1.14-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a510c6bb19c81587be0ef1e72ab75228bde70d1fef1f2ca62a124b663987fbbb"}, + {file = "psycopg_binary-3.1.14-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e25448293623a0dc2fa32a1781e87d14da7cb608a1cfac27681cc640d9523a1e"}, + {file = "psycopg_binary-3.1.14-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:740f0914da1e4db4021969ff467ae2fcec7cac40ee58b3a32ffa8fe476658f8a"}, + {file = "psycopg_binary-3.1.14-cp37-cp37m-win_amd64.whl", hash = "sha256:d305d825b78a59f32bb6c1bca751d18ccc4bb9b80340c8d2d4c1924ab9d347d5"}, + {file = "psycopg_binary-3.1.14-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e67e5c4f08ce8bf59a54f957168423910bfb46feb469da13fc76d0c6dd377047"}, + {file = "psycopg_binary-3.1.14-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a171ba6aed4845da2f5c1205de498f9f51826f181ef86a2a1e1342c98d35859b"}, + {file = "psycopg_binary-3.1.14-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e612ab1ffb680ac928a641abd75d0b0fa5ea87421f0fdabd082baff55849b90e"}, + {file = "psycopg_binary-3.1.14-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c92805c868fc0fa629033f74eebef58f3d5d15e0d7981ec33a1cb4027a7cc62"}, + {file = "psycopg_binary-3.1.14-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4df14e755962b1324f70aadea2ff915dfdb6dfdf56d9cdb10aebd183d0dc83b7"}, + {file = "psycopg_binary-3.1.14-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e790397d555a438b7785e611bc3ae1f344f1f799507fd97005476e2bc816947"}, + {file = "psycopg_binary-3.1.14-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d49bbdcb49939e97d6a161b864272e5f5daf199034d996b506a1006c78611481"}, + {file = "psycopg_binary-3.1.14-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:19e34d464ceab28825f1e9e8c5b482d5da51b2c9a6e105901d384ea4e225edc1"}, + {file = "psycopg_binary-3.1.14-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b44af0b30eb4bd733446474eb4051d93c4f2eb30928cd4c4b2180d162fa35483"}, + {file = "psycopg_binary-3.1.14-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:95f212ce9c9c7f754e7d981978de1603ddeac15bb4b2434afccbc976c84a9529"}, + {file = "psycopg_binary-3.1.14-cp38-cp38-win_amd64.whl", hash = "sha256:545ea4b72321c25afa76aa5a7eeb04e22ac7d5ead9762d5f30849fe2fdf1217a"}, + {file = "psycopg_binary-3.1.14-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a9e8cda0acfcfe127b390a877ab43ca2c248adff2ad746f26db19f3025eb4ba3"}, + {file = "psycopg_binary-3.1.14-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:502a06d4037a2c27e67f2521322ec173335108d5ee303dd58129b3a5b358ed30"}, + {file = "psycopg_binary-3.1.14-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be20bcccca8326a9f060a6749fdf92bf6147e8b5039c7a6cd9f0f5fe7c190ec"}, + {file = "psycopg_binary-3.1.14-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:044f8eb0ae01588d2da80c12f897c7562b430e04aa841555fc478f370be34d1e"}, + {file = "psycopg_binary-3.1.14-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f67b956506958f12fa9a110f19d9ec2caf4c98708386c84b4622d7227b344f4"}, + {file = "psycopg_binary-3.1.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74ee5c67ad273cf0b4653f440afd69cff4d1a19684a52653ad6b10b270d4589b"}, + {file = "psycopg_binary-3.1.14-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b1780b065f3b85403dcf818a6397305eaa57206a4a5d195b9b3589bd818f757b"}, + {file = "psycopg_binary-3.1.14-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:32e9d8bdfcfef9ccbd916bcbb8bcfcb281613e5dec9f5dcc3e4bff3e2eb9d490"}, + {file = "psycopg_binary-3.1.14-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0f7014dd971a3c00d63244381107800d47eebf3b1fa4f95784e6e4fa4d77d3c2"}, + {file = "psycopg_binary-3.1.14-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98409e33c7a2907c5cdf172d8f83c5664cd3ed99122a9c43b8d6e915c3beb050"}, + {file = "psycopg_binary-3.1.14-cp39-cp39-win_amd64.whl", hash = "sha256:d7a52d5bfd09a89872a192ad4bbf0dd2a1de07ceaa420337c35061376606310b"}, ] [[package]] @@ -1184,6 +1296,20 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + [[package]] name = "python-dotenv" version = "1.0.0" @@ -1234,6 +1360,17 @@ files = [ [package.extras] dev = ["atomicwrites (==1.2.1)", "attrs (==19.2.0)", "coverage (==6.5.0)", "hatch", "invoke (==1.7.3)", "more-itertools (==4.3.0)", "pbr (==4.3.0)", "pluggy (==1.0.0)", "py (==1.11.0)", "pytest (==7.2.0)", "pytest-cov (==4.0.0)", "pytest-timeout (==2.1.0)", "pyyaml (==5.1)"] +[[package]] +name = "pytz" +version = "2023.3.post1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, + {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, +] + [[package]] name = "pyyaml" version = "6.0" @@ -1506,13 +1643,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.22.0" +version = "0.24.0.post1" description = "The lightning-fast ASGI server." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "uvicorn-0.22.0-py3-none-any.whl", hash = "sha256:e9434d3bbf05f310e762147f769c9f21235ee118ba2d2bf1155a7196448bd996"}, - {file = "uvicorn-0.22.0.tar.gz", hash = "sha256:79277ae03db57ce7d9aa0567830bbb51d7a612f54d6e1e3e92da3ef24c2c8ed8"}, + {file = "uvicorn-0.24.0.post1-py3-none-any.whl", hash = "sha256:7c84fea70c619d4a710153482c0d230929af7bcf76c7bfa6de151f0a3a80121e"}, + {file = "uvicorn-0.24.0.post1.tar.gz", hash = "sha256:09c8e5a79dc466bdf28dead50093957db184de356fcdc48697bad3bde4c2588e"}, ] [package.dependencies] @@ -1522,6 +1659,7 @@ h11 = ">=0.8" httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} @@ -1689,4 +1827,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "fd7d68658f669b68f1b10f61315ec59a9c5e5ef5b42b2506ba933a8c955e8e08" +content-hash = "0ea43acd3f3f058a7640f58d5183d834b88b2da78c07d593beb572892d3d59b5" diff --git a/pyproject.toml b/pyproject.toml index 7398b52..cb3e54a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,34 +1,34 @@ [tool.poetry] name = "geneweaver-api" -version = "0.0.1a0" +version = "0.0.1a1" description = "description" authors = ["Jax Computational Sciences "] packages = [ - {include = "geneweaver/api", from = "src"}, - {include = "geneweaver/db", from = "src"} + {include = "geneweaver/api", from = "src"} ] [tool.poetry.dependencies] python = "^3.9" -geneweaver-core = "^0.2.0a0" +geneweaver-core = "^0.8.0a0" fastapi = {extras = ["all"], version = "^0.99.1"} -uvicorn = {extras = ["standard"], version = "^0.22.0"} -geneweaver-db = "^0.1.0a0" +uvicorn = {extras = ["standard"], version = "^0.24.0"} +geneweaver-db = "^0.2.0a0" psycopg-pool = "^3.1.7" requests = "^2.31.0" python-jose = {extras = ["cryptography"], version = "^3.3.0"} [tool.poetry.group.dev.dependencies] -geneweaver-testing = "^0.0.1b0" +geneweaver-testing = "^0.0.3" pytest-asyncio = "^0.21.0" [tool.ruff] select = ['F', 'E', 'W', 'A', 'C90', 'N', 'B', 'ANN', 'D', 'I', 'ERA', 'PD', 'NPY', 'PT'] +[tool.ruff.per-file-ignores] +"tests/*" = ["ANN001", "ANN201"] +"src/*" = ["ANN101"] +"src/geneweaver/api/controller/*" = ["B008"] + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" - - - - From 90a56bd51e5ad5e9ae93f210ea85f5eaaccdaf9d Mon Sep 17 00:00:00 2001 From: Alexander Berger Date: Tue, 5 Dec 2023 12:57:03 -0500 Subject: [PATCH 13/13] Updating readme to better reflect dev setup process --- README.MD | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/README.MD b/README.MD index 55fadd8..4b3f74c 100644 --- a/README.MD +++ b/README.MD @@ -1,7 +1,26 @@ -## Project Name: geneweaver-api +# Geneweaver 3 API -Description: description +Description: The API for the Geneweaver v3 application ecosystem. -App: geneweaver_api +## Setup -To run the app execute `uvicorn geneweaver_api.main:app --reload` \ No newline at end of file +### Local + +#### Requirements + +- Python Poetry +- Python 3.9 or higher +- A connection to a copy of the Geneweaver Database + +#### Setup + +1. Clone the repository +2. Run `poetry install` in project root to install dependencies +3. Configure environment settings with environment variables or a `.env` file. +4. Run the application + +To run the app execute either `uvicorn geneweaver.api.main:app --reload` with the poetry +virtualenv activated, or just run `poetry run uvicorn geneweaver.api.main:app --reload`. + +This will host the application on `http://127.0.0.1:8000/` which means the swagger docs +page is available at `http://127.0.0.1:8000/docs`.