diff --git a/.github/workflows/pull-request-workflow.yml b/.github/workflows/pull-request-workflow.yml deleted file mode 100644 index 54eb30c..0000000 --- a/.github/workflows/pull-request-workflow.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Pre-merge Review - -on: [pull_request] - -jobs: - pull-request-checks: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Setup python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Install dependencies - run: pip install poetry && poetry install --sync - - name: Run styling - run: make style-check - - name: Run type checks - run: make type-check - - name: Start services - run: IS_CI="True" make init - - name: Check migration scripts are added for modified schemas - run: make check-db-schema - - name: Run tests - env: - TEST_STRIPE_SECRET_KEY: ${{ secrets.TEST_STRIPE_SECRET_KEY }} - run: | - echo STRIPE_SECRET_KEY=${{ secrets.TEST_STRIPE_SECRET_KEY }} >> .env.local - echo STRIPE_DEVICE_NAME=CI >> .env.local - DEPLOYMENT_ENV=testing poetry run pytest - - name: cleanup - if: ${{ always() }} - run: | - docker compose -f env-prep/docker-compose-dev.yml -p vlm-project down --rmi all --volumes - docker system prune --force --volumes diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 0000000..e3f7beb --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,119 @@ +name: Pre-merge Review + +on: [pull_request] + +jobs: + setup: + runs-on: ubuntu-latest + outputs: + python-cache-key: ${{ steps.cache-deps.outputs.cache-hit }} + steps: + # Step 1: Checkout code + - name: Checkout code + uses: actions/checkout@v4 + + # Step 2: Set up Python with caching + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Cache Python dependencies + id: cache-deps + uses: actions/cache@v3 + with: + path: | + ~/.cache/pypoetry/virtualenvs + .venv + key: ${{ runner.os }}-pip-${{ hashFiles('**/poetry.lock') }} + restore-keys: | + ${{ runner.os }}-pip- + + # Step 3: Install dependencies + - name: Install dependencies + run: | + pip install poetry + poetry sync + + lint: + needs: setup + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Python cache + uses: actions/cache@v3 + with: + path: | + ~/.cache/pypoetry/virtualenvs + .venv + key: ${{ runner.os }}-pip-${{ hashFiles('**/poetry.lock') }} + + - name: Install Poetry + run: | + pip install poetry + + - name: Run styling checks + run: make style-check + + type-check: + needs: setup + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Python cache + uses: actions/cache@v3 + with: + path: | + ~/.cache/pypoetry/virtualenvs + .venv + key: ${{ runner.os }}-pip-${{ hashFiles('**/poetry.lock') }} + + - name: Install Poetry + run: | + pip install poetry + + - name: Run type checks + run: make type-check + + test: + needs: setup + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Python cache + uses: actions/cache@v3 + with: + path: | + ~/.cache/pypoetry/virtualenvs + .venv + key: ${{ runner.os }}-pip-${{ hashFiles('**/poetry.lock') }} + + - name: Install Poetry + run: | + pip install poetry + + - name: Start services + run: | + IS_CI="True" make init + + - name: Check database schema migrations + run: make check-db-schema + + - name: Run tests + env: + TEST_STRIPE_SECRET_KEY: ${{ secrets.TEST_STRIPE_SECRET_KEY }} + run: | + echo STRIPE_SECRET_KEY=${{ secrets.TEST_STRIPE_SECRET_KEY }} >> .env.local + echo STRIPE_DEVICE_NAME=CI >> .env.local + DEPLOYMENT_ENV=testing poetry run pytest + - name: Cleanup resources + if: ${{ always() }} + run: | + make kill + docker system prune --force --volumes diff --git a/Dockerfile b/Dockerfile index 8682812..f4142e3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12-bookworm as builder +FROM python:3.12-bookworm AS builder RUN pip install poetry==1.4.2 @@ -14,7 +14,7 @@ RUN poetry add psycopg2-binary RUN --mount=type=cache,target=$POETRY_CACHE_DIR poetry install --without dev --no-root -FROM python:3.12-slim-bookworm as runtime +FROM python:3.12-slim-bookworm AS runtime ENV VIRTUAL_ENV=/app/.venv \ PATH="/app/.venv/bin:$PATH" diff --git a/Makefile b/Makefile index 0fa108a..edcd6c5 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ dev-p: poetry run uvicorn virtual_labs.api:app --reload init: - ./dev-init.sh + ./dev-init.sh $(filter-out $@,$(MAKECMDGOALS)) kill: cd env-prep && docker compose -f docker-compose-dev.yml -p vlm-project down --remove-orphans --volumes diff --git a/README.md b/README.md index 6e06154..fc0554d 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,24 @@ Make sure you have the following dependencies installed: - [jq](https://jqlang.github.io/jq/download/) - Stripe API key (`STRIPE_SECRET_KEY` test key and `STRIPE_DEVICE_NAME=dev` in `./.env.local`) +# Configuration + +**Running in Macos (M4):** +For users running the virtual-lab-api on macOS with the M4 chip, the docker-compose configuration requires specific updates to ensure compatibility with the architecture and resource management. Please apply the following changes: + +**Delta service:** +Add the following property to ensure the service runs using the correct architecture: +``` +platform: linux/amd64 +``` + +**Elasticsearch service:** +Include the following environment variables to configure memory usage and disable SVE (Scalable Vector Extensions): +``` +ES_JAVA_OPTS: "-Xms512m -Xmx512m -XX:UseSVE=0" +CLI_JAVA_OPTS: "-XX:UseSVE=0" +``` + # Development 1. Install the dependencies @@ -24,6 +42,10 @@ Make sure you have the following dependencies installed: ```bash make init ``` + If you are using a machine with Apple ships (as M4), use this command instead: + ``` + make init amd + ``` 3. Run db migrations (this also initializes the database) ``` @@ -33,6 +55,7 @@ Make sure you have the following dependencies installed: This should start the server on port 8000 (http://127.0.0.1:8000) The docs will be available at http://127.0.0.1:8000/docs#/ + # Retrieving tokens for test users The token for user `test` (only user right now that can create virtual labs) is already copied to your clipboard when you run `make init`. @@ -109,7 +132,6 @@ For further details on working with Setup Intents and managing payment methods, stripe setup_intents confirm seti_1PFtBwFjhkSGAqrAUHCvTAAA \ --payment-method=pm_card_visa ``` - # IDE Setup ## VS Code diff --git a/dev-init.sh b/dev-init.sh index b664d2f..1b5ef3a 100755 --- a/dev-init.sh +++ b/dev-init.sh @@ -8,11 +8,16 @@ KC_REALM_NAME="obp-realm" CLIENT_ID="obpapp" CLIENT_SECRET="obp-secret" +if [ "$1" == "amd" ]; then + COMPOSE_FILE="env-prep/docker-compose-dev-amd.yml" +else + COMPOSE_FILE="env-prep/docker-compose-dev.yml" +fi # Start containers chmod +x ./env-prep/init/init-aws.sh ls -l ./env-prep/init/init-aws.sh -docker compose -f env-prep/docker-compose-dev.yml -p vlm-project up --wait +docker compose -f "$COMPOSE_FILE" -p vlm-project up --wait # Check that delta ready to accept connections echo "Checking that delta is ready to accept connections..." @@ -35,6 +40,7 @@ curl -XPUT \ }' echo "Initialize nexus" + python3 env-prep/init/init.py echo "📦 Initialize Vl database" diff --git a/env-prep/docker-compose-dev-amd.yml b/env-prep/docker-compose-dev-amd.yml new file mode 100644 index 0000000..c896a43 --- /dev/null +++ b/env-prep/docker-compose-dev-amd.yml @@ -0,0 +1,210 @@ +volumes: + keycloak_data: {} + vlm_data: {} + delta_data: {} + +networks: + ls: + ipam: + config: + # Specify the subnet range for IP address allocation + - subnet: 10.0.2.0/24 + +services: + keycloak-db: + image: postgres:latest + container_name: keycloak-db + environment: + POSTGRES_USER: keycloak + POSTGRES_PASSWORD: keycloak + POSTGRES_DB: keycloak + healthcheck: + test: ["CMD-SHELL", "pg_isready -U keycloak"] + interval: 10s + timeout: 5s + retries: 3 + restart: unless-stopped + volumes: + - keycloak_data:/var/lib/postgresql/data + networks: + - ls + + keycloak: + image: quay.io/keycloak/keycloak:24.0 + container_name: keycloak + environment: + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: admin + DB_VENDOR: postgres + DB_ADDR: keycloak-db + DB_DATABASE: keycloak + DB_USER: keycloak + DB_PASSWORD: keycloak + depends_on: + - keycloak-db + command: + - start-dev + - --http-port=9090 + - --hostname=keycloak + - --hostname-port=9090 + - --hostname-strict-backchannel=true + - --import-realm + ports: + - "9090:9090" + networks: + - ls + volumes: + - ./realm-export.json:/opt/keycloak/data/import/realm-import.json + + virtual-lab-db: + image: postgres:latest + container_name: vlm-db + environment: + POSTGRES_USER: vlm + POSTGRES_PASSWORD: vlm + PGDATA: vlm_data + POSTGRES_DB: vlm + ports: + - 15432:5432 + healthcheck: + test: ["CMD-SHELL", "pg_isready -U vlm"] + interval: 10s + timeout: 5s + retries: 3 + restart: unless-stopped + volumes: + - vlm_data:/var/lib/postgresql/data + networks: + - ls + + mailpit: + image: axllent/mailpit + container_name: test-mail-server + restart: always + volumes: + - ./email-data:/email-data + ports: + - 8025:8025 # UI to see sent emails + - 1025:1025 # test smtp server + environment: + MP_MAX_MESSAGES: 5000 + MP_DATA_FILE: /email-data/mailpit.db + MP_SMTP_AUTH_ACCEPT_ANY: 1 + MP_SMTP_AUTH_ALLOW_INSECURE: 1 + + delta: + container_name: delta + platform: linux/amd64 + depends_on: + keycloak: + condition: service_started + elasticsearch: + condition: service_healthy + blazegraph: + condition: service_started + postgres: + condition: service_started + localstack: + condition: service_started + environment: + DELTA_PLUGINS: "/opt/docker/plugins/" + DELTA_EXTERNAL_CONF: "/config/delta-postgres.conf" + KAMON_ENABLED: "false" + image: bluebrain/nexus-delta:latest + entrypoint: + - '/bin/bash' + - '-c' + - '/opt/docker/bin/delta-app -Xmx4G' + ports: + - 8080:8080 + volumes: + - ./config:/config + - /tmp:/default-volume + dns: + # Set the DNS server to be the LocalStack container, for host resolution + - 10.0.2.20 + networks: + - ls + + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:8.12.1 + environment: + ES_JAVA_OPTS: "-Xms512m -Xmx512m -XX:UseSVE=0" + CLI_JAVA_OPTS: "-XX:UseSVE=0" + discovery.type: "single-node" + bootstrap.memory_lock: "true" + xpack.security.enabled: "true" + ingest.geoip.downloader.enabled: "false" + ELASTIC_PASSWORD: "password" + healthcheck: + test: [ "CMD", "curl", "-f", "http://elastic:password@localhost:9200/" ] + interval: 1s + timeout: 2s + retries: 60 + ports: + - 9200:9200 + deploy: + resources: + limits: + memory: 4G + networks: + - ls + + blazegraph: + image: bluebrain/blazegraph-nexus:2.1.6-RC + environment: + JAVA_OPTS: "-DjettyXml=/config/jetty.xml -Djava.awt.headless=true -XX:MaxDirectMemorySize=300m -Xms4g -Xmx4g -XX:+UseG1GC" + ports: + - 9999:9999 + volumes: + - ./config:/config + networks: + - ls + + postgres: + image: library/postgres:15.6 + environment: + POSTGRES_USER: "postgres" + POSTGRES_PASSWORD: "postgres" + ports: + - 5432:5432 + volumes: + - delta_data:/var/lib/postgresql/data + networks: + - ls + + stripe-cli: + image: stripe/stripe-cli + container_name: stripe + environment: + STRIPE_CLI_TELEMETRY_OPTOUT: 1 + STRIPE_API_KEY: ${STRIPE_SECRET_KEY} + STRIPE_DEVICE_NAME: ${STRIPE_DEVICE_NAME} + volumes: + - ./stripe-data:/stripe-data + entrypoint: + - '/bin/sh' + - '-c' + - | + echo STRIPE_WEBHOOK_SECRET=$(stripe listen --print-secret) > /stripe-data/.env.local && + stripe listen --forward-to http://host.docker.internal:8000/payments/webhook + network_mode: host + extra_hosts: + - "host.docker.internal:host-gateway" + - "172.17.0.1:host-gateway" + + localstack: + image: localstack/localstack:3.5 + environment: + AWS_ACCESS_KEY_ID: "MY_ACCESS_KEY" + AWS_SECRET_ACCESS_KEY: "CHUTCHUT" + SERVICES: "s3:4566" + ports: + - 4566:4566 + volumes: + - "./init/init-aws.sh:/etc/localstack/init/ready.d/init-aws.sh" # ready hook + - "/var/run/docker.sock:/var/run/docker.sock" + networks: + ls: + # Set the container IP address in the 10.0.2.0/24 subnet + ipv4_address: 10.0.2.20 diff --git a/env-prep/docker-compose-dev.yml b/env-prep/docker-compose-dev.yml index 71da581..58b7a3c 100644 --- a/env-prep/docker-compose-dev.yml +++ b/env-prep/docker-compose-dev.yml @@ -1,5 +1,3 @@ -version: "3.9" - volumes: keycloak_data: {} vlm_data: {} @@ -105,8 +103,6 @@ services: condition: service_started postgres: condition: service_started - storage-service: - condition: service_started localstack: condition: service_started environment: @@ -117,9 +113,7 @@ services: entrypoint: - '/bin/bash' - '-c' - - | - ln -sf /opt/docker/plugins/disabled/project-deletion.jar /opt/docker/plugins/project-deletion.jar && - /opt/docker/bin/delta-app -Xmx4G + - '/opt/docker/bin/delta-app -Xmx4G' ports: - 8080:8080 volumes: @@ -134,7 +128,7 @@ services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.12.1 environment: - ES_JAVA_OPTS: "-Xmx2G" + ES_JAVA_OPTS: "-Xms2G" discovery.type: "single-node" bootstrap.memory_lock: "true" xpack.security.enabled: "true" @@ -196,21 +190,7 @@ services: extra_hosts: - "host.docker.internal:host-gateway" - "172.17.0.1:host-gateway" - - storage-service: - container_name: "nexus-storage-service" - image: bluebrain/nexus-storage:latest - environment: - STORAGE_CONFIG_FILE: "/config/storage.conf" - KAMON_ENABLED: "false" - entrypoint: [ "./bin/storage", - "-Dkamon.modules.prometheus-reporter.enabled=false", - "-Dkamon.modules.jaeger.enabled=false" ] - ports: - - 8090:8090 - volumes: - - ./config:/config - + localstack: image: localstack/localstack:3.5 environment: diff --git a/poetry.lock b/poetry.lock index 6a1b133..2fc19b1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. [[package]] name = "aiosmtplib" @@ -6,6 +6,7 @@ version = "2.0.2" description = "asyncio SMTP client" optional = false python-versions = ">=3.7,<4.0" +groups = ["main"] files = [ {file = "aiosmtplib-2.0.2-py3-none-any.whl", hash = "sha256:1e631a7a3936d3e11c6a144fb8ffd94bb4a99b714f2cb433e825d88b698e37bc"}, {file = "aiosmtplib-2.0.2.tar.gz", hash = "sha256:138599a3227605d29a9081b646415e9e793796ca05322a78f69179f0135016a3"}, @@ -21,6 +22,7 @@ version = "1.13.2" description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "alembic-1.13.2-py3-none-any.whl", hash = "sha256:6b8733129a6224a9a711e17c99b08462dbf7cc9670ba8f2e2ae9af860ceb1953"}, {file = "alembic-1.13.2.tar.gz", hash = "sha256:1ff0ae32975f4fd96028c39ed9bb3c867fe3af956bd7bb37343b54c9fe7445ef"}, @@ -40,6 +42,7 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -51,6 +54,7 @@ version = "4.4.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, @@ -71,6 +75,7 @@ version = "0.2.2" description = "Python decorator for async properties." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "async_property-0.2.2-py2.py3-none-any.whl", hash = "sha256:8924d792b5843994537f8ed411165700b27b2bd966cefc4daeefc1253442a9d7"}, {file = "async_property-0.2.2.tar.gz", hash = "sha256:17d9bd6ca67e27915a75d92549df64b5c7174e9dc806b30a3934dc4ff0506380"}, @@ -82,6 +87,7 @@ version = "0.29.0" description = "An asyncio PostgreSQL driver" optional = false python-versions = ">=3.8.0" +groups = ["main"] files = [ {file = "asyncpg-0.29.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72fd0ef9f00aeed37179c62282a3d14262dbbafb74ec0ba16e1b1864d8a12169"}, {file = "asyncpg-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52e8f8f9ff6e21f9b39ca9f8e3e33a5fcdceaf5667a8c5c32bee158e313be385"}, @@ -136,6 +142,7 @@ version = "0.29.1" description = "asyncpg stubs" optional = false python-versions = ">=3.8,<4.0" +groups = ["main"] files = [ {file = "asyncpg_stubs-0.29.1-py3-none-any.whl", hash = "sha256:cce994d5a19394249e74ae8d252bde3c77cee0ddfc776cc708b724fdb4adebb6"}, {file = "asyncpg_stubs-0.29.1.tar.gz", hash = "sha256:686afcc0af3a2f3c8e393cd850e0de430e5a139ce82b2f28ef8f693ecdf918bf"}, @@ -151,6 +158,7 @@ version = "1.8.2" description = "Fast, simple object-to-object and broadcast signaling" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"}, {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, @@ -162,6 +170,7 @@ version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, @@ -173,6 +182,8 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -252,6 +263,7 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -263,6 +275,7 @@ version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" +groups = ["main", "dev"] files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, @@ -362,6 +375,7 @@ version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, @@ -376,10 +390,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "platform_system == \"Windows\" or sys_platform == \"win32\"", dev = "sys_platform == \"win32\""} [[package]] name = "cryptography" @@ -387,6 +403,7 @@ version = "43.0.1" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"}, {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"}, @@ -436,6 +453,7 @@ version = "5.5" description = "This package provides a DateTime data type, as known from Zope. Unless you need to communicate with Zope APIs, you're probably better off using Python's built-in datetime module." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "DateTime-5.5-py3-none-any.whl", hash = "sha256:0abf6c51cb4ba7cee775ca46ccc727f3afdde463be28dbbe8803631fefd4a120"}, {file = "DateTime-5.5.tar.gz", hash = "sha256:21ec6331f87a7fcb57bd7c59e8a68bfffe6fcbf5acdbbc7b356d6a9a020191d3"}, @@ -451,6 +469,7 @@ version = "2.1.0" description = "A library to handle automated deprecations" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a"}, {file = "deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff"}, @@ -465,6 +484,7 @@ version = "0.3.8" description = "Distribution utilities" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, @@ -476,6 +496,7 @@ version = "2.6.1" description = "DNS toolkit" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, @@ -496,6 +517,7 @@ version = "2.2.0" description = "A robust email address syntax and deliverability validation library." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, @@ -511,6 +533,7 @@ version = "0.110.3" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "fastapi-0.110.3-py3-none-any.whl", hash = "sha256:fd7600612f755e4050beb74001310b5a7e1796d149c2ee363124abdfa0289d32"}, {file = "fastapi-0.110.3.tar.gz", hash = "sha256:555700b0159379e94fdbfc6bb66a0f1c43f4cf7060f25239af3d84b63a656626"}, @@ -541,6 +564,7 @@ version = "1.4.1" description = "Simple lightweight mail library for FastApi" optional = false python-versions = ">=3.8.1,<4.0" +groups = ["main"] files = [ {file = "fastapi_mail-1.4.1-py3-none-any.whl", hash = "sha256:fa5ef23b2dea4d3ba4587f4bbb53f8f15274124998fb4e40629b3b636c76c398"}, {file = "fastapi_mail-1.4.1.tar.gz", hash = "sha256:9095b713bd9d3abb02fe6d7abb637502aaf680b52e177d60f96273ef6bc8bb70"}, @@ -565,6 +589,7 @@ version = "3.16.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, @@ -581,6 +606,8 @@ version = "3.1.0" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")" files = [ {file = "greenlet-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a814dc3100e8a046ff48faeaa909e80cdb358411a3d6dd5293158425c684eda8"}, {file = "greenlet-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a771dc64fa44ebe58d65768d869fcfb9060169d203446c1d446e844b62bdfdca"}, @@ -660,6 +687,7 @@ version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -671,6 +699,7 @@ version = "1.0.5" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, @@ -692,6 +721,7 @@ version = "0.6.1" description = "A collection of framework independent HTTP protocol utils." optional = false python-versions = ">=3.8.0" +groups = ["main"] files = [ {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, @@ -740,6 +770,7 @@ version = "0.27.2" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, @@ -765,6 +796,7 @@ version = "2.6.0" description = "File identification library for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, @@ -779,6 +811,7 @@ version = "3.8" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, @@ -790,6 +823,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -801,6 +835,7 @@ version = "2.2.0" description = "Safely pass data to untrusted environments and back." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, @@ -812,6 +847,7 @@ version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, @@ -829,6 +865,7 @@ version = "1.5.6" description = "Implementation of JOSE Web standards" optional = false python-versions = ">= 3.8" +groups = ["main"] files = [ {file = "jwcrypto-1.5.6-py3-none-any.whl", hash = "sha256:150d2b0ebbdb8f40b77f543fb44ffd2baeff48788be71f67f03566692fd55789"}, {file = "jwcrypto-1.5.6.tar.gz", hash = "sha256:771a87762a0c081ae6166958a954f80848820b2ab066937dc8b8379d65b1b039"}, @@ -844,6 +881,7 @@ version = "0.7.2" description = "Python logging made (stupidly) simple" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"}, {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, @@ -862,6 +900,7 @@ version = "1.3.5" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"}, {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"}, @@ -881,6 +920,7 @@ version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, @@ -950,6 +990,7 @@ version = "1.11.2" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, @@ -996,6 +1037,7 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["main", "dev"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -1007,6 +1049,7 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -1018,6 +1061,7 @@ version = "3.10.7" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "orjson-3.10.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12"}, {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac"}, @@ -1084,6 +1128,7 @@ version = "24.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, @@ -1095,6 +1140,7 @@ version = "4.3.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617"}, {file = "platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c"}, @@ -1111,6 +1157,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -1126,6 +1173,7 @@ version = "3.8.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, @@ -1144,6 +1192,7 @@ version = "2.9.9" description = "psycopg2 - Python-PostgreSQL Database Adapter" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"}, {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"}, @@ -1225,6 +1274,8 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -1236,6 +1287,7 @@ version = "2.9.1" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612"}, {file = "pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2"}, @@ -1260,6 +1312,7 @@ version = "2.23.3" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic_core-2.23.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7f10a5d1b9281392f1bf507d16ac720e78285dfd635b05737c3911637601bae6"}, {file = "pydantic_core-2.23.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c09a7885dd33ee8c65266e5aa7fb7e2f23d49d8043f089989726391dd7350c5"}, @@ -1361,6 +1414,7 @@ version = "2.9.0" description = "Extra Pydantic types." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic_extra_types-2.9.0-py3-none-any.whl", hash = "sha256:f0bb975508572ba7bf3390b7337807588463b7248587e69f43b1ad7c797530d0"}, {file = "pydantic_extra_types-2.9.0.tar.gz", hash = "sha256:e061c01636188743bb69f368dcd391f327b8cfbfede2fe1cbb1211b06601ba3b"}, @@ -1383,6 +1437,7 @@ version = "2.5.2" description = "Settings management using Pydantic" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic_settings-2.5.2-py3-none-any.whl", hash = "sha256:2c912e55fd5794a59bf8c832b9de832dcfdf4778d79ff79b708744eed499a907"}, {file = "pydantic_settings-2.5.2.tar.gz", hash = "sha256:f90b139682bee4d2065273d5185d71d37ea46cfe57e1b5ae184fc6a0b2484ca0"}, @@ -1403,6 +1458,7 @@ version = "2.9.0" description = "JSON Web Token implementation in Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"}, {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, @@ -1420,6 +1476,7 @@ version = "8.0.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-8.0.2-py3-none-any.whl", hash = "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"}, {file = "pytest-8.0.2.tar.gz", hash = "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd"}, @@ -1440,6 +1497,7 @@ version = "0.21.1" description = "Pytest support for asyncio" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pytest-asyncio-0.21.1.tar.gz", hash = "sha256:40a7eae6dded22c7b604986855ea48400ab15b069ae38116e8c01238e9eeb64d"}, {file = "pytest_asyncio-0.21.1-py3-none-any.whl", hash = "sha256:8666c1c8ac02631d7c51ba282e0c69a8a452b211ffedf2599099845da5c5c37b"}, @@ -1458,6 +1516,7 @@ version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, @@ -1472,6 +1531,7 @@ version = "4.3.0" description = "python-keycloak is a Python package providing access to the Keycloak API." optional = false python-versions = "<4.0,>=3.8" +groups = ["main"] files = [ {file = "python_keycloak-4.3.0-py3-none-any.whl", hash = "sha256:6dc1e89c38346a90bb4386850381d84fedb489a7dea19d561aaad5882ceeed72"}, {file = "python_keycloak-4.3.0.tar.gz", hash = "sha256:c187ceee7d79f0d76d9c2ed26e3d10ce35e42a407896bf5ec15b8fb24fc0c9ce"}, @@ -1491,6 +1551,7 @@ version = "0.0.9" description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"}, {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, @@ -1505,6 +1566,7 @@ version = "2024.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, @@ -1516,6 +1578,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -1578,6 +1641,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -1599,6 +1663,7 @@ version = "1.0.0" description = "A utility belt for advanced users of python-requests" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] files = [ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, @@ -1613,6 +1678,7 @@ version = "0.2.2" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0a9efb032855ffb3c21f6405751d5e147b0c6b631e3ca3f6b20f917572b97eb6"}, {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d450b7fbff85913f866a5384d8912710936e2b96da74541c82c1b458472ddb39"}, @@ -1639,6 +1705,7 @@ version = "2.14.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "sentry_sdk-2.14.0-py2.py3-none-any.whl", hash = "sha256:b8bc3dc51d06590df1291b7519b85c75e2ced4f28d9ea655b6d54033503b5bf4"}, {file = "sentry_sdk-2.14.0.tar.gz", hash = "sha256:1e0e2eaf6dad918c7d1e0edac868a7bf20017b177f242cefe2a6bcd47955961d"}, @@ -1691,6 +1758,7 @@ version = "74.1.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "setuptools-74.1.2-py3-none-any.whl", hash = "sha256:5f4c08aa4d3ebcb57a50c33b1b07e94315d7fc7230f7115e47fc99776c8ce308"}, {file = "setuptools-74.1.2.tar.gz", hash = "sha256:95b40ed940a1c67eb70fc099094bd6e99c6ee7c23aa2306f4d2697ba7916f9c6"}, @@ -1711,6 +1779,7 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -1722,6 +1791,7 @@ version = "2.0.34" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "SQLAlchemy-2.0.34-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:95d0b2cf8791ab5fb9e3aa3d9a79a0d5d51f55b6357eecf532a120ba3b5524db"}, {file = "SQLAlchemy-2.0.34-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:243f92596f4fd4c8bd30ab8e8dd5965afe226363d75cab2468f2c707f64cd83b"}, @@ -1775,7 +1845,10 @@ files = [ ] [package.dependencies] -greenlet = {version = "!=0.4.17", optional = true, markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") or extra == \"asyncio\""} +greenlet = [ + {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}, + {version = "!=0.4.17", optional = true, markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") or extra == \"asyncio\""}, +] mypy = {version = ">=0.910", optional = true, markers = "extra == \"mypy\""} typing-extensions = ">=4.6.0" @@ -1810,6 +1883,7 @@ version = "0.37.2" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, @@ -1827,6 +1901,7 @@ version = "9.12.0" description = "Python bindings for the Stripe API" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "stripe-9.12.0-py2.py3-none-any.whl", hash = "sha256:14dd20ef1e6386a52e1f7aea07701fc6a80223706d72e3509e4966adb599e138"}, {file = "stripe-9.12.0.tar.gz", hash = "sha256:cbc526abd0f001c920c323ba7c40cce3dee1647d920e9dbecae3488f37367524"}, @@ -1842,6 +1917,7 @@ version = "2.32.0.20240907" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "types-requests-2.32.0.20240907.tar.gz", hash = "sha256:ff33935f061b5e81ec87997e91050f7b4af4f82027a7a7a9d9aaea04a963fdf8"}, {file = "types_requests-2.32.0.20240907-py3-none-any.whl", hash = "sha256:1d1e79faeaf9d42def77f3c304893dea17a97cae98168ac69f3cb465516ee8da"}, @@ -1856,6 +1932,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -1867,6 +1944,7 @@ version = "5.10.0" description = "Ultra fast JSON encoder and decoder for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"}, {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"}, @@ -1954,6 +2032,7 @@ version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, @@ -1971,6 +2050,7 @@ version = "1.30" description = "UUID object and generation functions (Python 2.3 or higher)" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "uuid-1.30.tar.gz", hash = "sha256:1f87cc004ac5120466f36c5beae48b4c48cc411968eed0eaecd3da82aa96193f"}, ] @@ -1981,6 +2061,7 @@ version = "0.27.1" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "uvicorn-0.27.1-py3-none-any.whl", hash = "sha256:5c89da2f3895767472a35556e539fd59f7edbe9b1e9c0e1c99eebeadc61838e4"}, {file = "uvicorn-0.27.1.tar.gz", hash = "sha256:3d9a267296243532db80c83a959a3400502165ade2c1338dea4e67915fd4745a"}, @@ -2006,6 +2087,8 @@ version = "0.20.0" description = "Fast implementation of asyncio event loop on top of libuv" optional = false python-versions = ">=3.8.0" +groups = ["main"] +markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\"" files = [ {file = "uvloop-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9ebafa0b96c62881d5cafa02d9da2e44c23f9f0cd829f3a32a6aff771449c996"}, {file = "uvloop-0.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:35968fc697b0527a06e134999eef859b4034b37aebca537daeb598b9d45a137b"}, @@ -2050,6 +2133,7 @@ version = "20.26.4" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "virtualenv-20.26.4-py3-none-any.whl", hash = "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55"}, {file = "virtualenv-20.26.4.tar.gz", hash = "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c"}, @@ -2070,6 +2154,7 @@ version = "0.24.0" description = "Simple, modern and high performance file watching and code reload in python." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "watchfiles-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:083dc77dbdeef09fa44bb0f4d1df571d2e12d8a8f985dccde71ac3ac9ac067a0"}, {file = "watchfiles-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e94e98c7cb94cfa6e071d401ea3342767f28eb5a06a58fafdc0d2a4974f4f35c"}, @@ -2165,6 +2250,7 @@ version = "13.0.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "websockets-13.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1841c9082a3ba4a05ea824cf6d99570a6a2d8849ef0db16e9c826acb28089e8f"}, {file = "websockets-13.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c5870b4a11b77e4caa3937142b650fbbc0914a3e07a0cf3131f35c0587489c1c"}, @@ -2260,6 +2346,8 @@ version = "1.1.0" description = "A small Python utility to set file creation time on Windows" optional = false python-versions = ">=3.5" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, @@ -2274,6 +2362,7 @@ version = "7.0.3" description = "Interfaces for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "zope.interface-7.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9b9369671a20b8d039b8e5a1a33abd12e089e319a3383b4cc0bf5c67bd05fe7b"}, {file = "zope.interface-7.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db6237e8fa91ea4f34d7e2d16d74741187e9105a63bbb5686c61fea04cdbacca"}, @@ -2320,6 +2409,6 @@ test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.12" -content-hash = "7a5c637634fa40128abb51988b39a41e7c31f44033fe0978bc30f18884503906" +content-hash = "25470feca8b1196b5b12730f34329c93eed9e9c54c0f7c81e1bb88dc20894203" diff --git a/pyproject.toml b/pyproject.toml index 233adae..f13f497 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ asyncpg = "^0.29.0" asyncpg-stubs = "^0.29.1" stripe = "^9.6.0" sentry-sdk = {extras = ["fastapi"], version = "^2.14.0"} +requests = "^2.32.3" [tool.poetry.group.dev.dependencies] diff --git a/virtual_labs/domain/user.py b/virtual_labs/domain/user.py index 6e8374d..d30b579 100644 --- a/virtual_labs/domain/user.py +++ b/virtual_labs/domain/user.py @@ -60,3 +60,4 @@ class UserAgentResponse(BaseModel): family_name: str name: str createdAt: datetime + type: list[str] diff --git a/virtual_labs/infrastructure/email/assets/logo.png b/virtual_labs/infrastructure/email/assets/logo.png index d3dc90b..7257e1b 100644 Binary files a/virtual_labs/infrastructure/email/assets/logo.png and b/virtual_labs/infrastructure/email/assets/logo.png differ diff --git a/virtual_labs/infrastructure/email/templates/invitation_template.html b/virtual_labs/infrastructure/email/templates/invitation_template.html index e9f6b62..a0e3e1d 100644 --- a/virtual_labs/infrastructure/email/templates/invitation_template.html +++ b/virtual_labs/infrastructure/email/templates/invitation_template.html @@ -70,6 +70,10 @@ /* ------------------------------------- HEADER, FOOTER, MAIN ------------------------------------- */ + .logo { + width: 147px; + height: 50px; + } .main { color: #003A8C; @@ -123,13 +127,14 @@ .invited-to { display: inline-block; padding: 0 15px; - padding-top: 4px; background-color: #003a8c; color: white; border-radius: 36px; line-height: 22px; text-align: center; + vertical-align: middle; padding-top: 4px; + padding-bottom: 4px; } .light { @@ -343,9 +348,9 @@  
- Open Blue Brain Platform + - Invitation to Blue Brain Open Platform {{origin}} + Invitation to Open Brain Platform {{origin}} @@ -394,12 +399,11 @@