From 5c9d52dab33977f5ce1120ad861b266632d90068 Mon Sep 17 00:00:00 2001 From: Andy Kuny Date: Wed, 24 Jan 2024 09:26:03 -0500 Subject: [PATCH 01/10] Set up dev deployment infrastructure --- .github/workflows/deploy-dev.yml | 36 ++ .gitignore | 5 +- .python-version | 1 + nad_ch/main.py => cli.py | 0 ...ande-gis-data-processing-via-task-queue.md | 22 + manifest-dev.yml | 13 + nad_ch/application_context.py | 99 ++- nad_ch/config.py | 34 +- nad_ch/infrastructure/database.py | 24 +- nad_ch/infrastructure/storage.py | 38 +- nad_ch/infrastructure/task_queue.py | 49 ++ poetry.lock | 577 +++++++++++++++++- pyproject.toml | 17 +- requirements.txt | 199 ++++++ runtime.txt | 1 + scripts/format.py | 2 +- scripts/{init_db.py => init_dev_db.py} | 6 +- tests/fakes.py | 6 +- tests/infrastructure/test_database.py | 37 +- web.py | 18 + 20 files changed, 1097 insertions(+), 87 deletions(-) create mode 100644 .github/workflows/deploy-dev.yml create mode 100644 .python-version rename nad_ch/main.py => cli.py (100%) create mode 100644 documentation/adr/0004-hande-gis-data-processing-via-task-queue.md create mode 100644 manifest-dev.yml create mode 100644 nad_ch/infrastructure/task_queue.py create mode 100644 requirements.txt create mode 100644 runtime.txt rename scripts/{init_db.py => init_dev_db.py} (71%) create mode 100644 web.py diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml new file mode 100644 index 0000000..fdf5634 --- /dev/null +++ b/.github/workflows/deploy-dev.yml @@ -0,0 +1,36 @@ +name: Deploy Dev + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + deploy-dev: + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.11.7 + + - name: Install poetry + shell: bash + run: | + curl -sSL https://install.python-poetry.org | python3 - + echo "/root/.local/bin" >> $GITHUB_PATH + + - name: Deploy to dev environment + uses: 18f/cg-deploy-action@main + with: + cf_username: ${{ secrets.CF_USERNAME }} + cf_password: ${{ secrets.CF_PASSWORD }} + cf_org: ${{ secrets.CF_ORG }} + cf_space: ${{ secrets.CF_SPACE }} + app_directory: ./app + push_arguments: "push -f manifest-dev.yml" diff --git a/.gitignore b/.gitignore index ef36a6e..c51dd98 100644 --- a/.gitignore +++ b/.gitignore @@ -163,4 +163,7 @@ cython_debug/ *.sqlite3 # Development storage -storage/ \ No newline at end of file +storage/ + +# Development task queue +celery_broker/ \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..d4b278f --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.11.7 diff --git a/nad_ch/main.py b/cli.py similarity index 100% rename from nad_ch/main.py rename to cli.py diff --git a/documentation/adr/0004-hande-gis-data-processing-via-task-queue.md b/documentation/adr/0004-hande-gis-data-processing-via-task-queue.md new file mode 100644 index 0000000..09f7717 --- /dev/null +++ b/documentation/adr/0004-hande-gis-data-processing-via-task-queue.md @@ -0,0 +1,22 @@ +# 4. Handle GIS Data Processing via Task Queue + +Date: 2024-01-23 + +## Status + +Accepted + +## Context + +In order to complete relatively compute-heavy and time-consuming GIS data analysis +tasks, without affecting the user's expereince, we will need to use a task queue +to manage these sorts of tasks. + +## Decision + +We will use Celery as the task queue for this project. + +## Consequences + +- **Architecture**: Use of Celery will be limited to the `infrastructure` + directory. diff --git a/manifest-dev.yml b/manifest-dev.yml new file mode 100644 index 0000000..0745799 --- /dev/null +++ b/manifest-dev.yml @@ -0,0 +1,13 @@ +--- +applications: + - name: nad-ch-dev + buildpacks: + - https://github.com/cloudfoundry/python-buildpack + services: + - nad-ch-dev-postgres + - nad-ch-dev-redis + - nad-ch-dev-s3 + random-route: true + memory: 256M + stack: cflinuxfs4 + command: poetry run start web diff --git a/nad_ch/application_context.py b/nad_ch/application_context.py index e578b33..3113358 100644 --- a/nad_ch/application_context.py +++ b/nad_ch/application_context.py @@ -1,13 +1,27 @@ import os import logging -from nad_ch.config import STORAGE_PATH +from nad_ch.config import ( + STORAGE_PATH, + DATABASE_URL_LOCAL, + QUEUE_BROKER_URL_LOCAL, + QUEUE_BACKEND_URL_LOCAL, + DATABASE_URL, + QUEUE_PASSWORD, + QUEUE_HOST, + QUEUE_PORT, + S3_ACCESS_KEY, + S3_SECRET_ACCESS_KEY, + S3_REGION, + S3_BUCKET_NAME +) from nad_ch.infrastructure.database import ( - session_scope, + create_session_factory, SqlAlchemyDataProviderRepository, SqlAlchemyDataSubmissionRepository, ) from nad_ch.infrastructure.logger import Logger -from nad_ch.infrastructure.storage import LocalStorage +from nad_ch.infrastructure.storage import LocalStorage, S3Storage +from nad_ch.infrastructure.task_queue import LocalTaskQueue, RedisTaskQueue from tests.fakes import ( FakeDataProviderRepository, FakeDataSubmissionRepository, @@ -17,10 +31,26 @@ class ApplicationContext: def __init__(self): - self._providers = SqlAlchemyDataProviderRepository(session_scope) - self._submissions = SqlAlchemyDataSubmissionRepository(session_scope) - self._logger = Logger(__name__) - self._storage = LocalStorage(STORAGE_PATH) + self._providers = self.create_provider_repository() + self._submissions = self.create_submission_repository() + self._logger = self.create_logger() + self._storage = self.create_storage() + self._task_queue = self.create_task_queue() + + def create_provider_repository(self): + return SqlAlchemyDataProviderRepository(create_session_factory(DATABASE_URL)) + + def create_submission_repository(self): + return SqlAlchemyDataSubmissionRepository(create_session_factory(DATABASE_URL)) + + def create_logger(self): + return Logger(__name__) + + def create_storage(self): + return S3Storage(S3_ACCESS_KEY, S3_SECRET_ACCESS_KEY, S3_REGION, S3_BUCKET_NAME) + + def create_task_queue(self): + return RedisTaskQueue("task-queue", QUEUE_PASSWORD, QUEUE_HOST, QUEUE_PORT) @property def providers(self): @@ -38,32 +68,51 @@ def logger(self): def storage(self): return self._storage + @property + def task_queue(self): + return self._task_queue -class TestApplicationContext(ApplicationContext): - def __init__(self): - self._providers = FakeDataProviderRepository() - self._submissions = FakeDataSubmissionRepository() - self._logger = Logger(__name__, logging.DEBUG) - self._storage = FakeStorage() - @property - def providers(self): - return self._providers +class DevLocalApplicationContext(ApplicationContext): + def create_provider_repository(self): + return SqlAlchemyDataProviderRepository( + create_session_factory(DATABASE_URL_LOCAL) + ) - @property - def submissions(self): - return self._submissions + def create_submission_repository(self): + return SqlAlchemyDataSubmissionRepository( + create_session_factory(DATABASE_URL_LOCAL) + ) - @property - def logger(self): - return self._logger + def create_logger(self): + return Logger(__name__, logging.DEBUG) - @property - def storage(self): - return self._storage + def create_storage(self): + return LocalStorage(STORAGE_PATH) + + def create_task_queue(self): + return LocalTaskQueue( + "local-task-queue", QUEUE_BROKER_URL_LOCAL, QUEUE_BACKEND_URL_LOCAL + ) + + +class TestApplicationContext(ApplicationContext): + def create_provider_repository(self): + return FakeDataProviderRepository() + + def create_submission_repository(self): + return FakeDataSubmissionRepository() + + def create_logger(self): + return Logger(__name__, logging.DEBUG) + + def create_storage(self): + return FakeStorage() def create_app_context(): if os.environ.get("APP_ENV") == "test": return TestApplicationContext() + if os.environ.get("APP_ENV") == "development-local": + return DevLocalApplicationContext() return ApplicationContext() diff --git a/nad_ch/config.py b/nad_ch/config.py index 3562569..d306d63 100644 --- a/nad_ch/config.py +++ b/nad_ch/config.py @@ -1,10 +1,42 @@ from dotenv import load_dotenv +import json import os +def get_credentials(service_name, default={}): + service = vcap_services.get(service_name, [default]) + return service[0].get("credentials", default) if service else default + + load_dotenv() +vcap_services = json.loads(os.getenv("VCAP_SERVICES", "{}")) + + +postgres_credentials = get_credentials("aws-rds") +redis_credentials = get_credentials("aws-elasticache-redis") +s3_credentials = get_credentials("s3") + + APP_ENV = os.getenv("APP_ENV") -DATABASE_URL = os.getenv("DATABASE_URL") +WEB_PORT = os.getenv("WEB_PORT") + +# Remote development config +DATABASE_URL = postgres_credentials.get("uri", os.getenv("DATABASE_URL")) +QUEUE_HOST = redis_credentials.get("hostname", os.getenv("QUEUE_HOST")) +QUEUE_PORT = redis_credentials.get("port", os.getenv("QUEUE_PORT")) +QUEUE_PASSWORD = redis_credentials.get("password", os.getenv("QUEUE_PASSWORD")) +S3_BUCKET_NAME = s3_credentials.get("bucket", os.getenv("S3_BUCKET_NAME")) +S3_ENDPOINT = s3_credentials.get("endpoint", os.getenv("S3_ENDPOINT")) +S3_ACCESS_KEY = s3_credentials.get("access_key_id", os.getenv("S3_ACCESS_KEY")) +S3_SECRET_ACCESS_KEY = s3_credentials.get( + "secret_access_key", os.getenv("S3_SECRET_ACCESS_KEY") +) +S3_REGION = s3_credentials.get("region", os.getenv("S3_REGION")) + +# Local development config STORAGE_PATH = os.getenv("STORAGE_PATH") +DATABASE_URL_LOCAL = os.getenv("DATABASE_URL_LOCAL") +QUEUE_BROKER_URL_LOCAL = os.getenv("QUEUE_BROKER_URL_LOCAL") +QUEUE_BACKEND_URL_LOCAL = os.getenv("QUEUE_BACKEND_URL_LOCAL") diff --git a/nad_ch/infrastructure/database.py b/nad_ch/infrastructure/database.py index 07d5cfc..3c79f49 100644 --- a/nad_ch/infrastructure/database.py +++ b/nad_ch/infrastructure/database.py @@ -1,20 +1,20 @@ from typing import List, Optional from sqlalchemy import Column, Integer, String, create_engine, ForeignKey, DateTime -from sqlalchemy.orm import sessionmaker, declarative_base, relationship +from sqlalchemy.orm import sessionmaker, declarative_base, relationship, Session from sqlalchemy.sql import func import contextlib -from nad_ch.config import DATABASE_URL from nad_ch.domain.entities import DataProvider, DataSubmission from nad_ch.domain.repositories import DataProviderRepository, DataSubmissionRepository -engine = create_engine(DATABASE_URL) -Session = sessionmaker(bind=engine) +def create_session_factory(connection_string: str): + engine = create_engine(connection_string) + return sessionmaker(bind=engine) @contextlib.contextmanager -def session_scope(): - session = Session() +def session_scope(session_factory): + session = session_factory try: yield session session.commit() @@ -82,9 +82,7 @@ def from_entity(submission): return model def to_entity(self, provider: DataProvider): - entity = DataSubmission( - id=self.id, filename=self.filename, provider=provider - ) + entity = DataSubmission(id=self.id, filename=self.filename, provider=provider) if self.created_at is not None: entity.set_created_at(self.created_at) @@ -96,8 +94,8 @@ def to_entity(self, provider: DataProvider): class SqlAlchemyDataProviderRepository(DataProviderRepository): - def __init__(self, session_factory): - self.session_factory = session_factory + def __init__(self, session: Session): + self.session_factory = session def add(self, provider: DataProvider) -> DataProvider: with self.session_factory() as session: @@ -124,8 +122,8 @@ def get_all(self) -> List[DataProvider]: class SqlAlchemyDataSubmissionRepository(DataSubmissionRepository): - def __init__(self, session_factory): - self.session_factory = session_factory + def __init__(self, session: Session): + self.session_factory = session def add(self, submission: DataSubmission) -> DataSubmission: with self.session_factory() as session: diff --git a/nad_ch/infrastructure/storage.py b/nad_ch/infrastructure/storage.py index 8de9c07..adaba30 100644 --- a/nad_ch/infrastructure/storage.py +++ b/nad_ch/infrastructure/storage.py @@ -1,7 +1,34 @@ import os +import boto3 import shutil +class S3Storage: + def __init__(self, access_key_id: str, secret_access_key: str, region: str, + bucket: str): + self.s3client = boto3.client( + "s3", + aws_access_key_id=access_key_id, + aws_secret_access_key=secret_access_key, + region_name=region, + ) + self.bucket_name = bucket + + def upload(self, source: str, destination: str) -> bool: + try: + self.s3client.upload_file(source, Bucket=self.bucket, Key=destination) + return True + except FileNotFoundError: + return False + + def delete(self, s3_key) -> bool: + try: + self.s3client.delete_object(Bucket=self.bucket, Key=s3_key) + return True + except Exception: + return False + + class LocalStorage: def __init__(self, base_path: str): self.base_path = base_path @@ -9,13 +36,14 @@ def __init__(self, base_path: str): def _full_path(self, path: str) -> str: return os.path.join(self.base_path, path) - def upload(self, source: str, destination: str) -> None: + def upload(self, source: str, destination: str) -> bool: shutil.copy(source, self._full_path(destination)) + return True - def delete(self, file_path: str) -> None: + def delete(self, file_path: str) -> bool: full_file_path = self._full_path(file_path) if os.path.exists(full_file_path): os.remove(full_file_path) - - def get_file_url(self, filename: str) -> str: - return filename + return True + else: + return False diff --git a/nad_ch/infrastructure/task_queue.py b/nad_ch/infrastructure/task_queue.py new file mode 100644 index 0000000..4165b30 --- /dev/null +++ b/nad_ch/infrastructure/task_queue.py @@ -0,0 +1,49 @@ +import os +from celery import Celery + + +class RedisTaskQueue: + def __init__(self, name: str, password: str, host: str, port: str): + redis_url = f"redis://:{password}@{host}:{port}/0" + + self.app = Celery(name, broker=redis_url, backend=redis_url) + + self.app.conf.update( + task_serializer="json", result_serializer="json", accept_content=["json"] + ) + + def define_task(self, func): + return self.app.task(func) + + def start_worker(self): + self.app.worker_main(argv=["worker", "--loglevel=info"]) + + +class LocalTaskQueue: + def __init__(self, name, base_path, backend_url): + broker_base_path = os.path.join(base_path, "celery_broker") + broker_url = f"filesystem://{broker_base_path}" + + self.app = Celery(name, broker=broker_url, backend=backend_url) + + self.app.conf.update( + broker_transport_options={ + "data_folder_in": os.path.join(broker_base_path, "in"), + "data_folder_out": os.path.join(broker_base_path, "out"), + "data_folder_processed": os.path.join(broker_base_path, "processed"), + }, + result_persistent=True, + task_serializer="json", + result_serializer="json", + accept_content=["json"], + ) + + os.makedirs(os.path.join(broker_base_path, "in"), exist_ok=True) + os.makedirs(os.path.join(broker_base_path, "out"), exist_ok=True) + os.makedirs(os.path.join(broker_base_path, "processed"), exist_ok=True) + + def define_task(self, func): + return self.app.task(func) + + def start_worker(self): + self.app.worker_main(argv=["worker", "--loglevel=info"]) diff --git a/poetry.lock b/poetry.lock index 3eb59cc..2f6d9a9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,40 @@ -# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. + +[[package]] +name = "amqp" +version = "5.2.0" +description = "Low-level AMQP client for Python (fork of amqplib)." +optional = false +python-versions = ">=3.6" +files = [ + {file = "amqp-5.2.0-py3-none-any.whl", hash = "sha256:827cb12fb0baa892aad844fd95258143bce4027fdac4fccddbc43330fd281637"}, + {file = "amqp-5.2.0.tar.gz", hash = "sha256:a1ecff425ad063ad42a486c902807d1482311481c8ad95a72694b2975e75f7fd"}, +] + +[package.dependencies] +vine = ">=5.0.0,<6.0.0" + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "billiard" +version = "4.2.0" +description = "Python multiprocessing fork with improvements and bugfixes" +optional = false +python-versions = ">=3.7" +files = [ + {file = "billiard-4.2.0-py3-none-any.whl", hash = "sha256:07aa978b308f334ff8282bd4a746e681b3513db5c9a514cbdd810cbbdc19714d"}, + {file = "billiard-4.2.0.tar.gz", hash = "sha256:9a3c3184cb275aa17a732f93f65b20c525d3d9f253722d26a82194803ade5a2c"}, +] [[package]] name = "black" @@ -44,6 +80,110 @@ d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] +[[package]] +name = "blinker" +version = "1.7.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.8" +files = [ + {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, + {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, +] + +[[package]] +name = "boto3" +version = "1.34.25" +description = "The AWS SDK for Python" +optional = false +python-versions = ">= 3.8" +files = [ + {file = "boto3-1.34.25-py3-none-any.whl", hash = "sha256:87532469188f1eeef4dca67dffbd3f0cc1d51cef7d5e5b5dc95d3b8125f8446e"}, + {file = "boto3-1.34.25.tar.gz", hash = "sha256:1b415e0553679ea05b9e2aed3eb271431011a67a165e3e0aefa323e13b8b7e92"}, +] + +[package.dependencies] +botocore = ">=1.34.25,<1.35.0" +jmespath = ">=0.7.1,<2.0.0" +s3transfer = ">=0.10.0,<0.11.0" + +[package.extras] +crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] + +[[package]] +name = "botocore" +version = "1.34.25" +description = "Low-level, data-driven core of boto 3." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "botocore-1.34.25-py3-none-any.whl", hash = "sha256:35dfab5bdb4620f73ac7c557c4e0d012429706d8760b100f099feea34b5505f8"}, + {file = "botocore-1.34.25.tar.gz", hash = "sha256:a39070bb760bd9545b0eef52a8bcb2d03918206e67a5a786ea4bd6f4bd949edd"}, +] + +[package.dependencies] +jmespath = ">=0.7.1,<2.0.0" +python-dateutil = ">=2.1,<3.0.0" +urllib3 = {version = ">=1.25.4,<2.1", markers = "python_version >= \"3.10\""} + +[package.extras] +crt = ["awscrt (==0.19.19)"] + +[[package]] +name = "celery" +version = "5.3.6" +description = "Distributed Task Queue." +optional = false +python-versions = ">=3.8" +files = [ + {file = "celery-5.3.6-py3-none-any.whl", hash = "sha256:9da4ea0118d232ce97dff5ed4974587fb1c0ff5c10042eb15278487cdd27d1af"}, + {file = "celery-5.3.6.tar.gz", hash = "sha256:870cc71d737c0200c397290d730344cc991d13a057534353d124c9380267aab9"}, +] + +[package.dependencies] +billiard = ">=4.2.0,<5.0" +click = ">=8.1.2,<9.0" +click-didyoumean = ">=0.3.0" +click-plugins = ">=1.1.1" +click-repl = ">=0.2.0" +kombu = ">=5.3.4,<6.0" +python-dateutil = ">=2.8.2" +tzdata = ">=2022.7" +vine = ">=5.1.0,<6.0" + +[package.extras] +arangodb = ["pyArango (>=2.0.2)"] +auth = ["cryptography (==41.0.5)"] +azureblockblob = ["azure-storage-blob (>=12.15.0)"] +brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] +cassandra = ["cassandra-driver (>=3.25.0,<4)"] +consul = ["python-consul2 (==0.1.5)"] +cosmosdbsql = ["pydocumentdb (==2.3.5)"] +couchbase = ["couchbase (>=3.0.0)"] +couchdb = ["pycouchdb (==1.14.2)"] +django = ["Django (>=2.2.28)"] +dynamodb = ["boto3 (>=1.26.143)"] +elasticsearch = ["elastic-transport (<=8.10.0)", "elasticsearch (<=8.11.0)"] +eventlet = ["eventlet (>=0.32.0)"] +gevent = ["gevent (>=1.5.0)"] +librabbitmq = ["librabbitmq (>=2.0.0)"] +memcache = ["pylibmc (==1.6.3)"] +mongodb = ["pymongo[srv] (>=4.0.2)"] +msgpack = ["msgpack (==1.0.7)"] +pymemcache = ["python-memcached (==1.59)"] +pyro = ["pyro4 (==4.82)"] +pytest = ["pytest-celery (==0.0.0)"] +redis = ["redis (>=4.5.2,!=4.5.5,<6.0.0)"] +s3 = ["boto3 (>=1.26.143)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +solar = ["ephem (==4.1.5)"] +sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] +sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.3.0)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] +tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=1.3.1)"] +zstd = ["zstandard (==0.22.0)"] + [[package]] name = "click" version = "8.1.7" @@ -58,6 +198,55 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "click-didyoumean" +version = "0.3.0" +description = "Enables git-like *did-you-mean* feature in click" +optional = false +python-versions = ">=3.6.2,<4.0.0" +files = [ + {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, + {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, +] + +[package.dependencies] +click = ">=7" + +[[package]] +name = "click-plugins" +version = "1.1.1" +description = "An extension module for click to enable registering CLI commands via setuptools entry-points." +optional = false +python-versions = "*" +files = [ + {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, + {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, +] + +[package.dependencies] +click = ">=4.0" + +[package.extras] +dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] + +[[package]] +name = "click-repl" +version = "0.3.0" +description = "REPL plugin for Click" +optional = false +python-versions = ">=3.6" +files = [ + {file = "click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9"}, + {file = "click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812"}, +] + +[package.dependencies] +click = ">=7.0" +prompt-toolkit = ">=3.0.36" + +[package.extras] +testing = ["pytest (>=7.2.1)", "pytest-cov (>=4.0.0)", "tox (>=4.4.3)"] + [[package]] name = "colorama" version = "0.4.6" @@ -149,6 +338,28 @@ mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.11.0,<2.12.0" pyflakes = ">=3.1.0,<3.2.0" +[[package]] +name = "flask" +version = "3.0.1" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.1-py3-none-any.whl", hash = "sha256:ca631a507f6dfe6c278ae20112cea3ff54ff2216390bf8880f6b035a5354af13"}, + {file = "flask-3.0.1.tar.gz", hash = "sha256:6489f51bb3666def6f314e15f19d50a1869a19ae0e8c9a3641ffe66c77d42403"}, +] + +[package.dependencies] +blinker = ">=1.6.2" +click = ">=8.1.3" +itsdangerous = ">=2.1.2" +Jinja2 = ">=3.1.2" +Werkzeug = ">=3.0.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + [[package]] name = "greenlet" version = "3.0.2" @@ -231,6 +442,77 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.7" +files = [ + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, +] + +[[package]] +name = "kombu" +version = "5.3.5" +description = "Messaging library for Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "kombu-5.3.5-py3-none-any.whl", hash = "sha256:0eac1bbb464afe6fb0924b21bf79460416d25d8abc52546d4f16cad94f789488"}, + {file = "kombu-5.3.5.tar.gz", hash = "sha256:30e470f1a6b49c70dc6f6d13c3e4cc4e178aa6c469ceb6bcd55645385fc84b93"}, +] + +[package.dependencies] +amqp = ">=5.1.1,<6.0.0" +vine = "*" + +[package.extras] +azureservicebus = ["azure-servicebus (>=7.10.0)"] +azurestoragequeues = ["azure-identity (>=1.12.0)", "azure-storage-queue (>=12.6.0)"] +confluentkafka = ["confluent-kafka (>=2.2.0)"] +consul = ["python-consul2"] +librabbitmq = ["librabbitmq (>=2.0.0)"] +mongodb = ["pymongo (>=4.1.1)"] +msgpack = ["msgpack"] +pyro = ["pyro4"] +qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] +redis = ["redis (>=4.5.2,!=4.5.5,<6.0.0)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] +sqs = ["boto3 (>=1.26.143)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=2.8.0)"] + [[package]] name = "logging" version = "0.4.9.6" @@ -241,6 +523,75 @@ files = [ {file = "logging-0.4.9.6.tar.gz", hash = "sha256:26f6b50773f085042d301085bd1bf5d9f3735704db9f37c1ce6d8b85c38f2417"}, ] +[[package]] +name = "markupsafe" +version = "2.1.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, +] + [[package]] name = "mccabe" version = "0.7.0" @@ -315,6 +666,101 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "prompt-toolkit" +version = "3.0.43" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, + {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "psycopg2-binary" +version = "2.9.9" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.7" +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"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"}, +] + [[package]] name = "pycodestyle" version = "2.11.0" @@ -391,6 +837,20 @@ pytest = ">=5.0" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] +[[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" @@ -405,6 +865,52 @@ files = [ [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "redis" +version = "5.0.1" +description = "Python client for Redis database and key-value store" +optional = false +python-versions = ">=3.7" +files = [ + {file = "redis-5.0.1-py3-none-any.whl", hash = "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f"}, + {file = "redis-5.0.1.tar.gz", hash = "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""} + +[package.extras] +hiredis = ["hiredis (>=1.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] + +[[package]] +name = "s3transfer" +version = "0.10.0" +description = "An Amazon S3 Transfer Manager" +optional = false +python-versions = ">= 3.8" +files = [ + {file = "s3transfer-0.10.0-py3-none-any.whl", hash = "sha256:3cdb40f5cfa6966e812209d0994f2a4709b561c88e90cf00c2696d2df4e56b2e"}, + {file = "s3transfer-0.10.0.tar.gz", hash = "sha256:d0c8bbf672d5eebbe4e57945e23b972d963f07d82f661cabf678a5c88831595b"}, +] + +[package.dependencies] +botocore = ">=1.33.2,<2.0a.0" + +[package.extras] +crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + [[package]] name = "sqlalchemy" version = "2.0.23" @@ -503,7 +1009,74 @@ files = [ {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] +[[package]] +name = "tzdata" +version = "2023.4" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2023.4-py2.py3-none-any.whl", hash = "sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3"}, + {file = "tzdata-2023.4.tar.gz", hash = "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9"}, +] + +[[package]] +name = "urllib3" +version = "2.0.7" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.7" +files = [ + {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, + {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "vine" +version = "5.1.0" +description = "Python promises." +optional = false +python-versions = ">=3.6" +files = [ + {file = "vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc"}, + {file = "vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0"}, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + +[[package]] +name = "werkzeug" +version = "3.0.1" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"}, + {file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "6a09bd6fc3f7b0f08b2142e9d457f85c3151e615acf28dd561a08ee2f745ef06" +content-hash = "757c2e850975243458fe53877753f6ea764708f6ccf7f975aa8155fbd057e090" diff --git a/pyproject.toml b/pyproject.toml index d69405b..fefad38 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,28 +12,34 @@ authors = [] python = "^3.11" click = "^8.1.7" sqlalchemy = "^2.0.23" -pytest-env = "^1.1.3" logging = "^0.4.9.6" +python-dotenv = "^1.0.0" +flask = "^3.0.1" +boto3 = "^1.34.25" +psycopg2-binary = "^2.9.9" +celery = "^5.3.6" +redis = "^5.0.1" [tool.poetry.group.dev.dependencies] pytest = "^7.4.2" flake8 = "^6.1.0" coverage = "^7.3.2" -python-dotenv = "^1.0.0" pytest-mock = "^3.12.0" black = "^23.12.1" +pytest-env = "^1.1.3" [tool.poetry.scripts] -init-db="scripts.init_db:main" +init-dev-db="scripts.init_dev_db:main" format = "scripts.format:main" lint = "flake8.main.cli:main" -start = "nad_ch.main:main" +start-cli = "cli:main" +start-web = "web:main" test = "pytest:main" [tool.pytest.ini_options] env = [ "APP_ENV=test", - "DATABASE_URL=sqlite:///:memory:" + "DATABASE_URL_LOCAL=sqlite:///:memory:" ] [tool.flake8] @@ -56,4 +62,3 @@ exclude = ''' | dist )/ ''' - diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..df119a7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,199 @@ +blinker==1.7.0 ; python_version >= "3.11" and python_version < "4.0" \ + --hash=sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9 \ + --hash=sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182 +click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" \ + --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ + --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de +colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and platform_system == "Windows" \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 +flask==3.0.1 ; python_version >= "3.11" and python_version < "4.0" \ + --hash=sha256:6489f51bb3666def6f314e15f19d50a1869a19ae0e8c9a3641ffe66c77d42403 \ + --hash=sha256:ca631a507f6dfe6c278ae20112cea3ff54ff2216390bf8880f6b035a5354af13 +greenlet==3.0.2 ; python_version >= "3.11" and python_version < "4.0" 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") \ + --hash=sha256:006c1028ac0cfcc4e772980cfe73f5476041c8c91d15d64f52482fc571149d46 \ + --hash=sha256:0acadbc3f72cb0ee85070e8d36bd2a4673d2abd10731ee73c10222cf2dd4713c \ + --hash=sha256:0c0fdb8142742ee68e97c106eb81e7d3e883cc739d9c5f2b28bc38a7bafeb6d1 \ + --hash=sha256:0df7eed98ea23b20e9db64d46eb05671ba33147df9405330695bcd81a73bb0c9 \ + --hash=sha256:10d247260db20887ae8857c0cbc750b9170f0b067dd7d38fb68a3f2334393bd3 \ + --hash=sha256:14b5d999aefe9ffd2049ad19079f733c3aaa426190ffecadb1d5feacef8fe397 \ + --hash=sha256:18fe39d70d482b22f0014e84947c5aaa7211fb8e13dc4cc1c43ed2aa1db06d9a \ + --hash=sha256:1c1129bc47266d83444c85a8e990ae22688cf05fb20d7951fd2866007c2ba9bc \ + --hash=sha256:1dac09e3c0b78265d2e6d3cbac2d7c48bd1aa4b04a8ffeda3adde9f1688df2c3 \ + --hash=sha256:2c93cd03acb1499ee4de675e1a4ed8eaaa7227f7949dc55b37182047b006a7aa \ + --hash=sha256:2e9c5423046eec21f6651268cb674dfba97280701e04ef23d312776377313206 \ + --hash=sha256:2ee59c4627c8c4bb3e15949fbcd499abd6b7f4ad9e0bfcb62c65c5e2cabe0ec4 \ + --hash=sha256:339c0272a62fac7e602e4e6ec32a64ff9abadc638b72f17f6713556ed011d493 \ + --hash=sha256:38878744926cec29b5cc3654ef47f3003f14bfbba7230e3c8492393fe29cc28b \ + --hash=sha256:3e4bfa752b3688d74ab1186e2159779ff4867644d2b1ebf16db14281f0445377 \ + --hash=sha256:520fcb53a39ef90f5021c77606952dbbc1da75d77114d69b8d7bded4a8e1a813 \ + --hash=sha256:5f9ea7c2c9795549653b6f7569f6bc75d2c7d1f6b2854eb8ce0bc6ec3cb2dd88 \ + --hash=sha256:654b84c9527182036747938b81938f1d03fb8321377510bc1854a9370418ab66 \ + --hash=sha256:6d65bec56a7bc352bcf11b275b838df618651109074d455a772d3afe25390b7d \ + --hash=sha256:7363756cc439a503505b67983237d1cc19139b66488263eb19f5719a32597836 \ + --hash=sha256:80d068e4b6e2499847d916ef64176811ead6bf210a610859220d537d935ec6fd \ + --hash=sha256:8756a94ed8f293450b0e91119eca2a36332deba69feb2f9ca410d35e74eae1e4 \ + --hash=sha256:89a6f6ddcbef4000cda7e205c4c20d319488ff03db961d72d4e73519d2465309 \ + --hash=sha256:8f34a765c5170c0673eb747213a0275ecc749ab3652bdbec324621ed5b2edaef \ + --hash=sha256:8f8d14a0a4e8c670fbce633d8b9a1ee175673a695475acd838e372966845f764 \ + --hash=sha256:950e21562818f9c771989b5b65f990e76f4ac27af66e1bb34634ae67886ede2a \ + --hash=sha256:9560c580c896030ff9c311c603aaf2282234643c90d1dec738a1d93e3e53cd51 \ + --hash=sha256:9acd8fd67c248b8537953cb3af8787c18a87c33d4dcf6830e410ee1f95a63fd4 \ + --hash=sha256:a37ae53cca36823597fd5f65341b6f7bac2dd69ecd6ca01334bb795460ab150b \ + --hash=sha256:aecea0442975741e7d69daff9b13c83caff8c13eeb17485afa65f6360a045765 \ + --hash=sha256:b1405614692ac986490d10d3e1a05e9734f473750d4bee3cf7d1286ef7af7da6 \ + --hash=sha256:b1fd25dfc5879a82103b3d9e43fa952e3026c221996ff4d32a9c72052544835d \ + --hash=sha256:b2cedf279ca38ef3f4ed0d013a6a84a7fc3d9495a716b84a5fc5ff448965f251 \ + --hash=sha256:b3f0497db77cfd034f829678b28267eeeeaf2fc21b3f5041600f7617139e6773 \ + --hash=sha256:bfcecc984d60b20ffe30173b03bfe9ba6cb671b0be1e95c3e2056d4fe7006590 \ + --hash=sha256:c1f647fe5b94b51488b314c82fdda10a8756d650cee8d3cd29f657c6031bdf73 \ + --hash=sha256:c235131bf59d2546bb3ebaa8d436126267392f2e51b85ff45ac60f3a26549af0 \ + --hash=sha256:c27b142a9080bdd5869a2fa7ebf407b3c0b24bd812db925de90e9afe3c417fd6 \ + --hash=sha256:c42bb589e6e9f9d8bdd79f02f044dff020d30c1afa6e84c0b56d1ce8a324553c \ + --hash=sha256:cd5bc4fde0842ff2b9cf33382ad0b4db91c2582db836793d58d174c569637144 \ + --hash=sha256:cecfdc950dd25f25d6582952e58521bca749cf3eeb7a9bad69237024308c8196 \ + --hash=sha256:d1fceb5351ab1601903e714c3028b37f6ea722be6873f46e349a960156c05650 \ + --hash=sha256:d4d0df07a38e41a10dfb62c6fc75ede196572b580f48ee49b9282c65639f3965 \ + --hash=sha256:d5547b462b8099b84746461e882a3eb8a6e3f80be46cb6afb8524eeb191d1a30 \ + --hash=sha256:d64643317e76b4b41fdba659e7eca29634e5739b8bc394eda3a9127f697ed4b0 \ + --hash=sha256:db4233358d3438369051a2f290f1311a360d25c49f255a6c5d10b5bcb3aa2b49 \ + --hash=sha256:e0e28f5233d64c693382f66d47c362b72089ebf8ac77df7e12ac705c9fa1163d \ + --hash=sha256:e79fb5a9fb2d0bd3b6573784f5e5adabc0b0566ad3180a028af99523ce8f6138 \ + --hash=sha256:e84bef3cfb6b6bfe258c98c519811c240dbc5b33a523a14933a252e486797c90 \ + --hash=sha256:ed1a8a08de7f68506a38f9a2ddb26bbd1480689e66d788fcd4b5f77e2d9ecfcc \ + --hash=sha256:ed9bf77b41798e8417657245b9f3649314218a4a17aefb02bb3992862df32495 \ + --hash=sha256:edf7a1daba1f7c54326291a8cde58da86ab115b78c91d502be8744f0aa8e3ffa \ + --hash=sha256:f260e6c2337871a52161824058923df2bbddb38bc11a5cbe71f3474d877c5bd9 \ + --hash=sha256:f27aa32466993c92d326df982c4acccd9530fe354e938d9e9deada563e71ce76 \ + --hash=sha256:f4cf532bf3c58a862196b06947b1b5cc55503884f9b63bf18582a75228d9950e \ + --hash=sha256:fb5d60805057d8948065338be6320d35e26b0a72f45db392eb32b70dd6dc9227 \ + --hash=sha256:fc14dd9554f88c9c1fe04771589ae24db76cd56c8f1104e4381b383d6b71aff8 \ + --hash=sha256:fefd5eb2c0b1adffdf2802ff7df45bfe65988b15f6b972706a0e55d451bffaea +itsdangerous==2.1.2 ; python_version >= "3.11" and python_version < "4.0" \ + --hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \ + --hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a +jinja2==3.1.3 ; python_version >= "3.11" and python_version < "4.0" \ + --hash=sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa \ + --hash=sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90 +logging==0.4.9.6 ; python_version >= "3.11" and python_version < "4.0" \ + --hash=sha256:26f6b50773f085042d301085bd1bf5d9f3735704db9f37c1ce6d8b85c38f2417 +markupsafe==2.1.3 ; python_version >= "3.11" and python_version < "4.0" \ + --hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \ + --hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \ + --hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \ + --hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \ + --hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \ + --hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \ + --hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \ + --hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \ + --hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \ + --hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \ + --hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \ + --hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \ + --hash=sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9 \ + --hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \ + --hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \ + --hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \ + --hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \ + --hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \ + --hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \ + --hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \ + --hash=sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac \ + --hash=sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52 \ + --hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \ + --hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \ + --hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \ + --hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \ + --hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \ + --hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \ + --hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \ + --hash=sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0 \ + --hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \ + --hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \ + --hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \ + --hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \ + --hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \ + --hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \ + --hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \ + --hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \ + --hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \ + --hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \ + --hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \ + --hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \ + --hash=sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad \ + --hash=sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee \ + --hash=sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc \ + --hash=sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2 \ + --hash=sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48 \ + --hash=sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7 \ + --hash=sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e \ + --hash=sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b \ + --hash=sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa \ + --hash=sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5 \ + --hash=sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e \ + --hash=sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb \ + --hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \ + --hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \ + --hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \ + --hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \ + --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \ + --hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11 +python-dotenv==1.0.0 ; python_version >= "3.11" and python_version < "4.0" \ + --hash=sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba \ + --hash=sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a +sqlalchemy==2.0.23 ; python_version >= "3.11" and python_version < "4.0" \ + --hash=sha256:0666031df46b9badba9bed00092a1ffa3aa063a5e68fa244acd9f08070e936d3 \ + --hash=sha256:0a8c6aa506893e25a04233bc721c6b6cf844bafd7250535abb56cb6cc1368884 \ + --hash=sha256:0e680527245895aba86afbd5bef6c316831c02aa988d1aad83c47ffe92655e74 \ + --hash=sha256:14aebfe28b99f24f8a4c1346c48bc3d63705b1f919a24c27471136d2f219f02d \ + --hash=sha256:1e018aba8363adb0599e745af245306cb8c46b9ad0a6fc0a86745b6ff7d940fc \ + --hash=sha256:227135ef1e48165f37590b8bfc44ed7ff4c074bf04dc8d6f8e7f1c14a94aa6ca \ + --hash=sha256:31952bbc527d633b9479f5f81e8b9dfada00b91d6baba021a869095f1a97006d \ + --hash=sha256:3e983fa42164577d073778d06d2cc5d020322425a509a08119bdcee70ad856bf \ + --hash=sha256:42d0b0290a8fb0165ea2c2781ae66e95cca6e27a2fbe1016ff8db3112ac1e846 \ + --hash=sha256:42ede90148b73fe4ab4a089f3126b2cfae8cfefc955c8174d697bb46210c8306 \ + --hash=sha256:4895a63e2c271ffc7a81ea424b94060f7b3b03b4ea0cd58ab5bb676ed02f4221 \ + --hash=sha256:4af79c06825e2836de21439cb2a6ce22b2ca129bad74f359bddd173f39582bf5 \ + --hash=sha256:5f94aeb99f43729960638e7468d4688f6efccb837a858b34574e01143cf11f89 \ + --hash=sha256:616fe7bcff0a05098f64b4478b78ec2dfa03225c23734d83d6c169eb41a93e55 \ + --hash=sha256:62d9e964870ea5ade4bc870ac4004c456efe75fb50404c03c5fd61f8bc669a72 \ + --hash=sha256:638c2c0b6b4661a4fd264f6fb804eccd392745c5887f9317feb64bb7cb03b3ea \ + --hash=sha256:63bfc3acc970776036f6d1d0e65faa7473be9f3135d37a463c5eba5efcdb24c8 \ + --hash=sha256:6463aa765cf02b9247e38b35853923edbf2f6fd1963df88706bc1d02410a5577 \ + --hash=sha256:64ac935a90bc479fee77f9463f298943b0e60005fe5de2aa654d9cdef46c54df \ + --hash=sha256:683ef58ca8eea4747737a1c35c11372ffeb84578d3aab8f3e10b1d13d66f2bc4 \ + --hash=sha256:75eefe09e98043cff2fb8af9796e20747ae870c903dc61d41b0c2e55128f958d \ + --hash=sha256:787af80107fb691934a01889ca8f82a44adedbf5ef3d6ad7d0f0b9ac557e0c34 \ + --hash=sha256:7c424983ab447dab126c39d3ce3be5bee95700783204a72549c3dceffe0fc8f4 \ + --hash=sha256:7e0dc9031baa46ad0dd5a269cb7a92a73284d1309228be1d5935dac8fb3cae24 \ + --hash=sha256:87a3d6b53c39cd173990de2f5f4b83431d534a74f0e2f88bd16eabb5667e65c6 \ + --hash=sha256:89a01238fcb9a8af118eaad3ffcc5dedaacbd429dc6fdc43fe430d3a941ff965 \ + --hash=sha256:9585b646ffb048c0250acc7dad92536591ffe35dba624bb8fd9b471e25212a35 \ + --hash=sha256:964971b52daab357d2c0875825e36584d58f536e920f2968df8d581054eada4b \ + --hash=sha256:967c0b71156f793e6662dd839da54f884631755275ed71f1539c95bbada9aaab \ + --hash=sha256:9ca922f305d67605668e93991aaf2c12239c78207bca3b891cd51a4515c72e22 \ + --hash=sha256:a86cb7063e2c9fb8e774f77fbf8475516d270a3e989da55fa05d08089d77f8c4 \ + --hash=sha256:aeb397de65a0a62f14c257f36a726945a7f7bb60253462e8602d9b97b5cbe204 \ + --hash=sha256:b41f5d65b54cdf4934ecede2f41b9c60c9f785620416e8e6c48349ab18643855 \ + --hash=sha256:bd45a5b6c68357578263d74daab6ff9439517f87da63442d244f9f23df56138d \ + --hash=sha256:c14eba45983d2f48f7546bb32b47937ee2cafae353646295f0e99f35b14286ab \ + --hash=sha256:c1bda93cbbe4aa2aa0aa8655c5aeda505cd219ff3e8da91d1d329e143e4aff69 \ + --hash=sha256:c4722f3bc3c1c2fcc3702dbe0016ba31148dd6efcd2a2fd33c1b4897c6a19693 \ + --hash=sha256:c80c38bd2ea35b97cbf7c21aeb129dcbebbf344ee01a7141016ab7b851464f8e \ + --hash=sha256:cabafc7837b6cec61c0e1e5c6d14ef250b675fa9c3060ed8a7e38653bd732ff8 \ + --hash=sha256:cc1d21576f958c42d9aec68eba5c1a7d715e5fc07825a629015fe8e3b0657fb0 \ + --hash=sha256:d0f7fb0c7527c41fa6fcae2be537ac137f636a41b4c5a4c58914541e2f436b45 \ + --hash=sha256:d4041ad05b35f1f4da481f6b811b4af2f29e83af253bf37c3c4582b2c68934ab \ + --hash=sha256:d5578e6863eeb998980c212a39106ea139bdc0b3f73291b96e27c929c90cd8e1 \ + --hash=sha256:e3b5036aa326dc2df50cba3c958e29b291a80f604b1afa4c8ce73e78e1c9f01d \ + --hash=sha256:e599a51acf3cc4d31d1a0cf248d8f8d863b6386d2b6782c5074427ebb7803bda \ + --hash=sha256:f3420d00d2cb42432c1d0e44540ae83185ccbbc67a6054dcc8ab5387add6620b \ + --hash=sha256:f48ed89dd11c3c586f45e9eec1e437b355b3b6f6884ea4a4c3111a3358fd0c18 \ + --hash=sha256:f508ba8f89e0a5ecdfd3761f82dda2a3d7b678a626967608f4273e0dba8f07ac \ + --hash=sha256:fd54601ef9cc455a0c61e5245f690c8a3ad67ddb03d3b91c361d076def0b4c60 +typing-extensions==4.9.0 ; python_version >= "3.11" and python_version < "4.0" \ + --hash=sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783 \ + --hash=sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd +werkzeug==3.0.1 ; python_version >= "3.11" and python_version < "4.0" \ + --hash=sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc \ + --hash=sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10 diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000..d45f665 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-3.11.7 \ No newline at end of file diff --git a/scripts/format.py b/scripts/format.py index dc2f77b..59acc31 100644 --- a/scripts/format.py +++ b/scripts/format.py @@ -2,7 +2,7 @@ def main(): - subprocess.run(["black", "."]) + subprocess.run(["black", "./nad_ch"]) if __name__ == "__main__": diff --git a/scripts/init_db.py b/scripts/init_dev_db.py similarity index 71% rename from scripts/init_db.py rename to scripts/init_dev_db.py index 1c19411..7ce65ef 100644 --- a/scripts/init_db.py +++ b/scripts/init_dev_db.py @@ -1,14 +1,14 @@ import os from sqlalchemy import create_engine from nad_ch.infrastructure.database import ModelBase -from nad_ch.config import DATABASE_URL +from nad_ch.config import DATABASE_URL_LOCAL def main(): - engine = create_engine(DATABASE_URL) + engine = create_engine(DATABASE_URL_LOCAL) # Check if the database file already exists - if os.path.exists(DATABASE_URL.split("///")[1]): + if os.path.exists(DATABASE_URL_LOCAL.split("///")[1]): print("Database already exists.") else: # Create all tables diff --git a/tests/fakes.py b/tests/fakes.py index 14352cf..a9d6081 100644 --- a/tests/fakes.py +++ b/tests/fakes.py @@ -43,8 +43,6 @@ class FakeStorage: def __init__(self): self._files = set() - def upload(self, source: str, destination: str) -> None: + def upload(self, source: str, destination: str) -> bool: self._files.add(destination) - - def get_file_url(self, filename: str) -> str: - return filename + return True diff --git a/tests/infrastructure/test_database.py b/tests/infrastructure/test_database.py index 975c2a8..8ca7212 100644 --- a/tests/infrastructure/test_database.py +++ b/tests/infrastructure/test_database.py @@ -1,8 +1,7 @@ import pytest -import contextlib from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from nad_ch.config import DATABASE_URL +from nad_ch.config import DATABASE_URL_LOCAL from nad_ch.domain.entities import DataProvider, DataSubmission from nad_ch.infrastructure.database import ( ModelBase, @@ -12,34 +11,22 @@ @pytest.fixture(scope="function") -def test_session(): - engine = create_engine(DATABASE_URL) +def test_database(): + engine = create_engine(DATABASE_URL_LOCAL, echo=True) ModelBase.metadata.create_all(engine) - Session = sessionmaker(bind=engine) - - @contextlib.contextmanager - def test_session_scope(): - session = Session() - try: - yield session - session.commit() - except Exception: - session.rollback() - raise - finally: - session.close() - - return test_session_scope + return engine @pytest.fixture(scope="function") -def providers(test_session): - return SqlAlchemyDataProviderRepository(test_session) +def providers(test_database): + Session = sessionmaker(bind=test_database) + return SqlAlchemyDataProviderRepository(Session) @pytest.fixture(scope="function") -def submissions(test_session): - return SqlAlchemyDataSubmissionRepository(test_session) +def submissions(test_database): + Session = sessionmaker(bind=test_database) + return SqlAlchemyDataSubmissionRepository(Session) def test_add_data_provider_to_repository_and_get_by_name(providers): @@ -77,9 +64,7 @@ def test_retrieve_a_list_of_submissions_by_provider(providers, submissions): saved_provider = providers.add(new_provider) new_submission = DataSubmission("some-file-name", saved_provider) submissions.add(new_submission) - another_new_submission = DataSubmission( - "some-other-file-name", saved_provider - ) + another_new_submission = DataSubmission("some-other-file-name", saved_provider) submissions.add(another_new_submission) submissions = submissions.get_by_provider(saved_provider) diff --git a/web.py b/web.py new file mode 100644 index 0000000..13eea93 --- /dev/null +++ b/web.py @@ -0,0 +1,18 @@ +from flask import Flask +from nad_ch.config import WEB_PORT + + +app = Flask(__name__) + + +@app.route('/') +def home(): + return 'Welcome to the NAD Collaboration Hub' + + +def main(): + app.run(host='0.0.0.0', port=int(WEB_PORT)) + + +if __name__ == '__main__': + main() From 498d8d09873949c1e260d65c2c44c6adb2c1352d Mon Sep 17 00:00:00 2001 From: Andy Kuny Date: Wed, 24 Jan 2024 09:36:27 -0500 Subject: [PATCH 02/10] Remove unnecessary requirements.txt file --- requirements.txt | 199 ----------------------------------------------- 1 file changed, 199 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index df119a7..0000000 --- a/requirements.txt +++ /dev/null @@ -1,199 +0,0 @@ -blinker==1.7.0 ; python_version >= "3.11" and python_version < "4.0" \ - --hash=sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9 \ - --hash=sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182 -click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" \ - --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ - --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de -colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and platform_system == "Windows" \ - --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ - --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 -flask==3.0.1 ; python_version >= "3.11" and python_version < "4.0" \ - --hash=sha256:6489f51bb3666def6f314e15f19d50a1869a19ae0e8c9a3641ffe66c77d42403 \ - --hash=sha256:ca631a507f6dfe6c278ae20112cea3ff54ff2216390bf8880f6b035a5354af13 -greenlet==3.0.2 ; python_version >= "3.11" and python_version < "4.0" 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") \ - --hash=sha256:006c1028ac0cfcc4e772980cfe73f5476041c8c91d15d64f52482fc571149d46 \ - --hash=sha256:0acadbc3f72cb0ee85070e8d36bd2a4673d2abd10731ee73c10222cf2dd4713c \ - --hash=sha256:0c0fdb8142742ee68e97c106eb81e7d3e883cc739d9c5f2b28bc38a7bafeb6d1 \ - --hash=sha256:0df7eed98ea23b20e9db64d46eb05671ba33147df9405330695bcd81a73bb0c9 \ - --hash=sha256:10d247260db20887ae8857c0cbc750b9170f0b067dd7d38fb68a3f2334393bd3 \ - --hash=sha256:14b5d999aefe9ffd2049ad19079f733c3aaa426190ffecadb1d5feacef8fe397 \ - --hash=sha256:18fe39d70d482b22f0014e84947c5aaa7211fb8e13dc4cc1c43ed2aa1db06d9a \ - --hash=sha256:1c1129bc47266d83444c85a8e990ae22688cf05fb20d7951fd2866007c2ba9bc \ - --hash=sha256:1dac09e3c0b78265d2e6d3cbac2d7c48bd1aa4b04a8ffeda3adde9f1688df2c3 \ - --hash=sha256:2c93cd03acb1499ee4de675e1a4ed8eaaa7227f7949dc55b37182047b006a7aa \ - --hash=sha256:2e9c5423046eec21f6651268cb674dfba97280701e04ef23d312776377313206 \ - --hash=sha256:2ee59c4627c8c4bb3e15949fbcd499abd6b7f4ad9e0bfcb62c65c5e2cabe0ec4 \ - --hash=sha256:339c0272a62fac7e602e4e6ec32a64ff9abadc638b72f17f6713556ed011d493 \ - --hash=sha256:38878744926cec29b5cc3654ef47f3003f14bfbba7230e3c8492393fe29cc28b \ - --hash=sha256:3e4bfa752b3688d74ab1186e2159779ff4867644d2b1ebf16db14281f0445377 \ - --hash=sha256:520fcb53a39ef90f5021c77606952dbbc1da75d77114d69b8d7bded4a8e1a813 \ - --hash=sha256:5f9ea7c2c9795549653b6f7569f6bc75d2c7d1f6b2854eb8ce0bc6ec3cb2dd88 \ - --hash=sha256:654b84c9527182036747938b81938f1d03fb8321377510bc1854a9370418ab66 \ - --hash=sha256:6d65bec56a7bc352bcf11b275b838df618651109074d455a772d3afe25390b7d \ - --hash=sha256:7363756cc439a503505b67983237d1cc19139b66488263eb19f5719a32597836 \ - --hash=sha256:80d068e4b6e2499847d916ef64176811ead6bf210a610859220d537d935ec6fd \ - --hash=sha256:8756a94ed8f293450b0e91119eca2a36332deba69feb2f9ca410d35e74eae1e4 \ - --hash=sha256:89a6f6ddcbef4000cda7e205c4c20d319488ff03db961d72d4e73519d2465309 \ - --hash=sha256:8f34a765c5170c0673eb747213a0275ecc749ab3652bdbec324621ed5b2edaef \ - --hash=sha256:8f8d14a0a4e8c670fbce633d8b9a1ee175673a695475acd838e372966845f764 \ - --hash=sha256:950e21562818f9c771989b5b65f990e76f4ac27af66e1bb34634ae67886ede2a \ - --hash=sha256:9560c580c896030ff9c311c603aaf2282234643c90d1dec738a1d93e3e53cd51 \ - --hash=sha256:9acd8fd67c248b8537953cb3af8787c18a87c33d4dcf6830e410ee1f95a63fd4 \ - --hash=sha256:a37ae53cca36823597fd5f65341b6f7bac2dd69ecd6ca01334bb795460ab150b \ - --hash=sha256:aecea0442975741e7d69daff9b13c83caff8c13eeb17485afa65f6360a045765 \ - --hash=sha256:b1405614692ac986490d10d3e1a05e9734f473750d4bee3cf7d1286ef7af7da6 \ - --hash=sha256:b1fd25dfc5879a82103b3d9e43fa952e3026c221996ff4d32a9c72052544835d \ - --hash=sha256:b2cedf279ca38ef3f4ed0d013a6a84a7fc3d9495a716b84a5fc5ff448965f251 \ - --hash=sha256:b3f0497db77cfd034f829678b28267eeeeaf2fc21b3f5041600f7617139e6773 \ - --hash=sha256:bfcecc984d60b20ffe30173b03bfe9ba6cb671b0be1e95c3e2056d4fe7006590 \ - --hash=sha256:c1f647fe5b94b51488b314c82fdda10a8756d650cee8d3cd29f657c6031bdf73 \ - --hash=sha256:c235131bf59d2546bb3ebaa8d436126267392f2e51b85ff45ac60f3a26549af0 \ - --hash=sha256:c27b142a9080bdd5869a2fa7ebf407b3c0b24bd812db925de90e9afe3c417fd6 \ - --hash=sha256:c42bb589e6e9f9d8bdd79f02f044dff020d30c1afa6e84c0b56d1ce8a324553c \ - --hash=sha256:cd5bc4fde0842ff2b9cf33382ad0b4db91c2582db836793d58d174c569637144 \ - --hash=sha256:cecfdc950dd25f25d6582952e58521bca749cf3eeb7a9bad69237024308c8196 \ - --hash=sha256:d1fceb5351ab1601903e714c3028b37f6ea722be6873f46e349a960156c05650 \ - --hash=sha256:d4d0df07a38e41a10dfb62c6fc75ede196572b580f48ee49b9282c65639f3965 \ - --hash=sha256:d5547b462b8099b84746461e882a3eb8a6e3f80be46cb6afb8524eeb191d1a30 \ - --hash=sha256:d64643317e76b4b41fdba659e7eca29634e5739b8bc394eda3a9127f697ed4b0 \ - --hash=sha256:db4233358d3438369051a2f290f1311a360d25c49f255a6c5d10b5bcb3aa2b49 \ - --hash=sha256:e0e28f5233d64c693382f66d47c362b72089ebf8ac77df7e12ac705c9fa1163d \ - --hash=sha256:e79fb5a9fb2d0bd3b6573784f5e5adabc0b0566ad3180a028af99523ce8f6138 \ - --hash=sha256:e84bef3cfb6b6bfe258c98c519811c240dbc5b33a523a14933a252e486797c90 \ - --hash=sha256:ed1a8a08de7f68506a38f9a2ddb26bbd1480689e66d788fcd4b5f77e2d9ecfcc \ - --hash=sha256:ed9bf77b41798e8417657245b9f3649314218a4a17aefb02bb3992862df32495 \ - --hash=sha256:edf7a1daba1f7c54326291a8cde58da86ab115b78c91d502be8744f0aa8e3ffa \ - --hash=sha256:f260e6c2337871a52161824058923df2bbddb38bc11a5cbe71f3474d877c5bd9 \ - --hash=sha256:f27aa32466993c92d326df982c4acccd9530fe354e938d9e9deada563e71ce76 \ - --hash=sha256:f4cf532bf3c58a862196b06947b1b5cc55503884f9b63bf18582a75228d9950e \ - --hash=sha256:fb5d60805057d8948065338be6320d35e26b0a72f45db392eb32b70dd6dc9227 \ - --hash=sha256:fc14dd9554f88c9c1fe04771589ae24db76cd56c8f1104e4381b383d6b71aff8 \ - --hash=sha256:fefd5eb2c0b1adffdf2802ff7df45bfe65988b15f6b972706a0e55d451bffaea -itsdangerous==2.1.2 ; python_version >= "3.11" and python_version < "4.0" \ - --hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \ - --hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a -jinja2==3.1.3 ; python_version >= "3.11" and python_version < "4.0" \ - --hash=sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa \ - --hash=sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90 -logging==0.4.9.6 ; python_version >= "3.11" and python_version < "4.0" \ - --hash=sha256:26f6b50773f085042d301085bd1bf5d9f3735704db9f37c1ce6d8b85c38f2417 -markupsafe==2.1.3 ; python_version >= "3.11" and python_version < "4.0" \ - --hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \ - --hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \ - --hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \ - --hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \ - --hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \ - --hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \ - --hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \ - --hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \ - --hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \ - --hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \ - --hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \ - --hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \ - --hash=sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9 \ - --hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \ - --hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \ - --hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \ - --hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \ - --hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \ - --hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \ - --hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \ - --hash=sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac \ - --hash=sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52 \ - --hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \ - --hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \ - --hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \ - --hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \ - --hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \ - --hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \ - --hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \ - --hash=sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0 \ - --hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \ - --hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \ - --hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \ - --hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \ - --hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \ - --hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \ - --hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \ - --hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \ - --hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \ - --hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \ - --hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \ - --hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \ - --hash=sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad \ - --hash=sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee \ - --hash=sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc \ - --hash=sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2 \ - --hash=sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48 \ - --hash=sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7 \ - --hash=sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e \ - --hash=sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b \ - --hash=sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa \ - --hash=sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5 \ - --hash=sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e \ - --hash=sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb \ - --hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \ - --hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \ - --hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \ - --hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \ - --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \ - --hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11 -python-dotenv==1.0.0 ; python_version >= "3.11" and python_version < "4.0" \ - --hash=sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba \ - --hash=sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a -sqlalchemy==2.0.23 ; python_version >= "3.11" and python_version < "4.0" \ - --hash=sha256:0666031df46b9badba9bed00092a1ffa3aa063a5e68fa244acd9f08070e936d3 \ - --hash=sha256:0a8c6aa506893e25a04233bc721c6b6cf844bafd7250535abb56cb6cc1368884 \ - --hash=sha256:0e680527245895aba86afbd5bef6c316831c02aa988d1aad83c47ffe92655e74 \ - --hash=sha256:14aebfe28b99f24f8a4c1346c48bc3d63705b1f919a24c27471136d2f219f02d \ - --hash=sha256:1e018aba8363adb0599e745af245306cb8c46b9ad0a6fc0a86745b6ff7d940fc \ - --hash=sha256:227135ef1e48165f37590b8bfc44ed7ff4c074bf04dc8d6f8e7f1c14a94aa6ca \ - --hash=sha256:31952bbc527d633b9479f5f81e8b9dfada00b91d6baba021a869095f1a97006d \ - --hash=sha256:3e983fa42164577d073778d06d2cc5d020322425a509a08119bdcee70ad856bf \ - --hash=sha256:42d0b0290a8fb0165ea2c2781ae66e95cca6e27a2fbe1016ff8db3112ac1e846 \ - --hash=sha256:42ede90148b73fe4ab4a089f3126b2cfae8cfefc955c8174d697bb46210c8306 \ - --hash=sha256:4895a63e2c271ffc7a81ea424b94060f7b3b03b4ea0cd58ab5bb676ed02f4221 \ - --hash=sha256:4af79c06825e2836de21439cb2a6ce22b2ca129bad74f359bddd173f39582bf5 \ - --hash=sha256:5f94aeb99f43729960638e7468d4688f6efccb837a858b34574e01143cf11f89 \ - --hash=sha256:616fe7bcff0a05098f64b4478b78ec2dfa03225c23734d83d6c169eb41a93e55 \ - --hash=sha256:62d9e964870ea5ade4bc870ac4004c456efe75fb50404c03c5fd61f8bc669a72 \ - --hash=sha256:638c2c0b6b4661a4fd264f6fb804eccd392745c5887f9317feb64bb7cb03b3ea \ - --hash=sha256:63bfc3acc970776036f6d1d0e65faa7473be9f3135d37a463c5eba5efcdb24c8 \ - --hash=sha256:6463aa765cf02b9247e38b35853923edbf2f6fd1963df88706bc1d02410a5577 \ - --hash=sha256:64ac935a90bc479fee77f9463f298943b0e60005fe5de2aa654d9cdef46c54df \ - --hash=sha256:683ef58ca8eea4747737a1c35c11372ffeb84578d3aab8f3e10b1d13d66f2bc4 \ - --hash=sha256:75eefe09e98043cff2fb8af9796e20747ae870c903dc61d41b0c2e55128f958d \ - --hash=sha256:787af80107fb691934a01889ca8f82a44adedbf5ef3d6ad7d0f0b9ac557e0c34 \ - --hash=sha256:7c424983ab447dab126c39d3ce3be5bee95700783204a72549c3dceffe0fc8f4 \ - --hash=sha256:7e0dc9031baa46ad0dd5a269cb7a92a73284d1309228be1d5935dac8fb3cae24 \ - --hash=sha256:87a3d6b53c39cd173990de2f5f4b83431d534a74f0e2f88bd16eabb5667e65c6 \ - --hash=sha256:89a01238fcb9a8af118eaad3ffcc5dedaacbd429dc6fdc43fe430d3a941ff965 \ - --hash=sha256:9585b646ffb048c0250acc7dad92536591ffe35dba624bb8fd9b471e25212a35 \ - --hash=sha256:964971b52daab357d2c0875825e36584d58f536e920f2968df8d581054eada4b \ - --hash=sha256:967c0b71156f793e6662dd839da54f884631755275ed71f1539c95bbada9aaab \ - --hash=sha256:9ca922f305d67605668e93991aaf2c12239c78207bca3b891cd51a4515c72e22 \ - --hash=sha256:a86cb7063e2c9fb8e774f77fbf8475516d270a3e989da55fa05d08089d77f8c4 \ - --hash=sha256:aeb397de65a0a62f14c257f36a726945a7f7bb60253462e8602d9b97b5cbe204 \ - --hash=sha256:b41f5d65b54cdf4934ecede2f41b9c60c9f785620416e8e6c48349ab18643855 \ - --hash=sha256:bd45a5b6c68357578263d74daab6ff9439517f87da63442d244f9f23df56138d \ - --hash=sha256:c14eba45983d2f48f7546bb32b47937ee2cafae353646295f0e99f35b14286ab \ - --hash=sha256:c1bda93cbbe4aa2aa0aa8655c5aeda505cd219ff3e8da91d1d329e143e4aff69 \ - --hash=sha256:c4722f3bc3c1c2fcc3702dbe0016ba31148dd6efcd2a2fd33c1b4897c6a19693 \ - --hash=sha256:c80c38bd2ea35b97cbf7c21aeb129dcbebbf344ee01a7141016ab7b851464f8e \ - --hash=sha256:cabafc7837b6cec61c0e1e5c6d14ef250b675fa9c3060ed8a7e38653bd732ff8 \ - --hash=sha256:cc1d21576f958c42d9aec68eba5c1a7d715e5fc07825a629015fe8e3b0657fb0 \ - --hash=sha256:d0f7fb0c7527c41fa6fcae2be537ac137f636a41b4c5a4c58914541e2f436b45 \ - --hash=sha256:d4041ad05b35f1f4da481f6b811b4af2f29e83af253bf37c3c4582b2c68934ab \ - --hash=sha256:d5578e6863eeb998980c212a39106ea139bdc0b3f73291b96e27c929c90cd8e1 \ - --hash=sha256:e3b5036aa326dc2df50cba3c958e29b291a80f604b1afa4c8ce73e78e1c9f01d \ - --hash=sha256:e599a51acf3cc4d31d1a0cf248d8f8d863b6386d2b6782c5074427ebb7803bda \ - --hash=sha256:f3420d00d2cb42432c1d0e44540ae83185ccbbc67a6054dcc8ab5387add6620b \ - --hash=sha256:f48ed89dd11c3c586f45e9eec1e437b355b3b6f6884ea4a4c3111a3358fd0c18 \ - --hash=sha256:f508ba8f89e0a5ecdfd3761f82dda2a3d7b678a626967608f4273e0dba8f07ac \ - --hash=sha256:fd54601ef9cc455a0c61e5245f690c8a3ad67ddb03d3b91c361d076def0b4c60 -typing-extensions==4.9.0 ; python_version >= "3.11" and python_version < "4.0" \ - --hash=sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783 \ - --hash=sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd -werkzeug==3.0.1 ; python_version >= "3.11" and python_version < "4.0" \ - --hash=sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc \ - --hash=sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10 From 9b029a8f439a3ad860f1b9a3ae0f7eec8b7c129b Mon Sep 17 00:00:00 2001 From: Andy Kuny Date: Wed, 24 Jan 2024 11:18:31 -0500 Subject: [PATCH 03/10] Update Celery ADR to include rationale --- .../adr/0004-hande-gis-data-processing-via-task-queue.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/documentation/adr/0004-hande-gis-data-processing-via-task-queue.md b/documentation/adr/0004-hande-gis-data-processing-via-task-queue.md index 09f7717..21d5ce5 100644 --- a/documentation/adr/0004-hande-gis-data-processing-via-task-queue.md +++ b/documentation/adr/0004-hande-gis-data-processing-via-task-queue.md @@ -14,9 +14,15 @@ to manage these sorts of tasks. ## Decision -We will use Celery as the task queue for this project. +We will use Celery as the task queue for this project. Celery is open source, +relatively straightforward to configure, and actively maintained. It also +allows tasks to be executed concurrently across multiple workers, which is +good for scaling. ## Consequences - **Architecture**: Use of Celery will be limited to the `infrastructure` directory. +- **Choice of Broker**: In order to leverage our remote development environment's + services, we will use Redis as a broker, keeping in mind the [caveats](https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/redis.html#caveats) + highlighted in Celery's documentation. From 15c7ef6b03383774c5f415f86db1f4663a54b674 Mon Sep 17 00:00:00 2001 From: Andy Kuny Date: Wed, 24 Jan 2024 11:52:34 -0500 Subject: [PATCH 04/10] Rework configuration logic --- .flake8 | 1 + manifest-dev.yml | 2 + nad_ch/application_context.py | 48 +++++++++---------- nad_ch/config/__init__.py | 7 +++ nad_ch/config/base.py | 9 ++++ nad_ch/config/development_local.py | 9 ++++ .../development_remote.py} | 16 +------ pyproject.toml | 2 +- tests/infrastructure/test_database.py | 4 +- 9 files changed, 55 insertions(+), 43 deletions(-) create mode 100644 nad_ch/config/__init__.py create mode 100644 nad_ch/config/base.py create mode 100644 nad_ch/config/development_local.py rename nad_ch/{config.py => config/development_remote.py} (76%) diff --git a/.flake8 b/.flake8 index b3e3f29..813fc6d 100644 --- a/.flake8 +++ b/.flake8 @@ -1,3 +1,4 @@ [flake8] max-line-length = 88 exclude = venv,.git,__pycache__,docs/source/conf.py,old,build,dist +ignore = F403, F401 diff --git a/manifest-dev.yml b/manifest-dev.yml index 0745799..99b7cd4 100644 --- a/manifest-dev.yml +++ b/manifest-dev.yml @@ -11,3 +11,5 @@ applications: memory: 256M stack: cflinuxfs4 command: poetry run start web + env: + APP_ENV: development-remote diff --git a/nad_ch/application_context.py b/nad_ch/application_context.py index 3113358..31ff34e 100644 --- a/nad_ch/application_context.py +++ b/nad_ch/application_context.py @@ -1,19 +1,5 @@ -import os import logging -from nad_ch.config import ( - STORAGE_PATH, - DATABASE_URL_LOCAL, - QUEUE_BROKER_URL_LOCAL, - QUEUE_BACKEND_URL_LOCAL, - DATABASE_URL, - QUEUE_PASSWORD, - QUEUE_HOST, - QUEUE_PORT, - S3_ACCESS_KEY, - S3_SECRET_ACCESS_KEY, - S3_REGION, - S3_BUCKET_NAME -) +import nad_ch.config as config from nad_ch.infrastructure.database import ( create_session_factory, SqlAlchemyDataProviderRepository, @@ -38,19 +24,23 @@ def __init__(self): self._task_queue = self.create_task_queue() def create_provider_repository(self): - return SqlAlchemyDataProviderRepository(create_session_factory(DATABASE_URL)) + return SqlAlchemyDataProviderRepository( + create_session_factory(config.DATABASE_URL)) def create_submission_repository(self): - return SqlAlchemyDataSubmissionRepository(create_session_factory(DATABASE_URL)) + return SqlAlchemyDataSubmissionRepository( + create_session_factory(config.DATABASE_URL)) def create_logger(self): return Logger(__name__) def create_storage(self): - return S3Storage(S3_ACCESS_KEY, S3_SECRET_ACCESS_KEY, S3_REGION, S3_BUCKET_NAME) + return S3Storage(config.S3_ACCESS_KEY, config.S3_SECRET_ACCESS_KEY, + config.S3_REGION, config.S3_BUCKET_NAME) def create_task_queue(self): - return RedisTaskQueue("task-queue", QUEUE_PASSWORD, QUEUE_HOST, QUEUE_PORT) + return RedisTaskQueue("task-queue", config.QUEUE_PASSWORD, + config.QUEUE_HOST, config.QUEUE_PORT) @property def providers(self): @@ -76,23 +66,23 @@ def task_queue(self): class DevLocalApplicationContext(ApplicationContext): def create_provider_repository(self): return SqlAlchemyDataProviderRepository( - create_session_factory(DATABASE_URL_LOCAL) + create_session_factory(config.DATABASE_URL) ) def create_submission_repository(self): return SqlAlchemyDataSubmissionRepository( - create_session_factory(DATABASE_URL_LOCAL) + create_session_factory(config.DATABASE_URL) ) def create_logger(self): return Logger(__name__, logging.DEBUG) def create_storage(self): - return LocalStorage(STORAGE_PATH) + return LocalStorage(config.STORAGE_PATH) def create_task_queue(self): return LocalTaskQueue( - "local-task-queue", QUEUE_BROKER_URL_LOCAL, QUEUE_BACKEND_URL_LOCAL + "local-task-queue", config.QUEUE_BROKER_URL, config.QUEUE_BACKEND_URL ) @@ -109,10 +99,16 @@ def create_logger(self): def create_storage(self): return FakeStorage() + def create_task_queue(self): + return LocalTaskQueue( + "test-task-queue", config.QUEUE_BROKER_URL, config.QUEUE_BACKEND_URL + ) + def create_app_context(): - if os.environ.get("APP_ENV") == "test": + if config.APP_ENV == "test": return TestApplicationContext() - if os.environ.get("APP_ENV") == "development-local": + elif config.APP_ENV == "development-local": return DevLocalApplicationContext() - return ApplicationContext() + else: + return ApplicationContext() diff --git a/nad_ch/config/__init__.py b/nad_ch/config/__init__.py new file mode 100644 index 0000000..98a627a --- /dev/null +++ b/nad_ch/config/__init__.py @@ -0,0 +1,7 @@ +from .base import APP_ENV + + +if APP_ENV == 'development-local' or APP_ENV == 'test': + from .development_local import * +elif APP_ENV == 'development-remote': + from .development_remote import * diff --git a/nad_ch/config/base.py b/nad_ch/config/base.py new file mode 100644 index 0000000..5180a78 --- /dev/null +++ b/nad_ch/config/base.py @@ -0,0 +1,9 @@ +import os +from dotenv import load_dotenv + + +load_dotenv() + + +APP_ENV = os.getenv("APP_ENV") +WEB_PORT = os.getenv("WEB_PORT", 3000) diff --git a/nad_ch/config/development_local.py b/nad_ch/config/development_local.py new file mode 100644 index 0000000..cfe63cc --- /dev/null +++ b/nad_ch/config/development_local.py @@ -0,0 +1,9 @@ +import os +from .base import * + + +# Local development config +STORAGE_PATH = os.getenv("STORAGE_PATH") +DATABASE_URL = os.getenv("DATABASE_URL") +QUEUE_BROKER_URL = os.getenv("QUEUE_BROKER_URL") +QUEUE_BACKEND_URL = os.getenv("QUEUE_BACKEND_URL") diff --git a/nad_ch/config.py b/nad_ch/config/development_remote.py similarity index 76% rename from nad_ch/config.py rename to nad_ch/config/development_remote.py index d306d63..310c857 100644 --- a/nad_ch/config.py +++ b/nad_ch/config/development_remote.py @@ -1,6 +1,6 @@ -from dotenv import load_dotenv import json import os +from .base import * def get_credentials(service_name, default={}): @@ -8,9 +8,7 @@ def get_credentials(service_name, default={}): return service[0].get("credentials", default) if service else default -load_dotenv() - - +# Remote development config vcap_services = json.loads(os.getenv("VCAP_SERVICES", "{}")) @@ -19,10 +17,6 @@ def get_credentials(service_name, default={}): s3_credentials = get_credentials("s3") -APP_ENV = os.getenv("APP_ENV") -WEB_PORT = os.getenv("WEB_PORT") - -# Remote development config DATABASE_URL = postgres_credentials.get("uri", os.getenv("DATABASE_URL")) QUEUE_HOST = redis_credentials.get("hostname", os.getenv("QUEUE_HOST")) QUEUE_PORT = redis_credentials.get("port", os.getenv("QUEUE_PORT")) @@ -34,9 +28,3 @@ def get_credentials(service_name, default={}): "secret_access_key", os.getenv("S3_SECRET_ACCESS_KEY") ) S3_REGION = s3_credentials.get("region", os.getenv("S3_REGION")) - -# Local development config -STORAGE_PATH = os.getenv("STORAGE_PATH") -DATABASE_URL_LOCAL = os.getenv("DATABASE_URL_LOCAL") -QUEUE_BROKER_URL_LOCAL = os.getenv("QUEUE_BROKER_URL_LOCAL") -QUEUE_BACKEND_URL_LOCAL = os.getenv("QUEUE_BACKEND_URL_LOCAL") diff --git a/pyproject.toml b/pyproject.toml index fefad38..4152212 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ test = "pytest:main" [tool.pytest.ini_options] env = [ "APP_ENV=test", - "DATABASE_URL_LOCAL=sqlite:///:memory:" + "DATABASE_URL=sqlite:///:memory:" ] [tool.flake8] diff --git a/tests/infrastructure/test_database.py b/tests/infrastructure/test_database.py index 8ca7212..d9bdd04 100644 --- a/tests/infrastructure/test_database.py +++ b/tests/infrastructure/test_database.py @@ -1,7 +1,7 @@ import pytest from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from nad_ch.config import DATABASE_URL_LOCAL +from nad_ch.config import DATABASE_URL from nad_ch.domain.entities import DataProvider, DataSubmission from nad_ch.infrastructure.database import ( ModelBase, @@ -12,7 +12,7 @@ @pytest.fixture(scope="function") def test_database(): - engine = create_engine(DATABASE_URL_LOCAL, echo=True) + engine = create_engine(DATABASE_URL, echo=True) ModelBase.metadata.create_all(engine) return engine From ae66b952423c482a6ac161f9fb709cd2d49b5c6c Mon Sep 17 00:00:00 2001 From: Andy Kuny Date: Wed, 24 Jan 2024 11:57:44 -0500 Subject: [PATCH 05/10] Update local task queue to create broker directory if it doesn\'t exist --- nad_ch/infrastructure/task_queue.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nad_ch/infrastructure/task_queue.py b/nad_ch/infrastructure/task_queue.py index 4165b30..a530a1d 100644 --- a/nad_ch/infrastructure/task_queue.py +++ b/nad_ch/infrastructure/task_queue.py @@ -22,6 +22,7 @@ def start_worker(self): class LocalTaskQueue: def __init__(self, name, base_path, backend_url): broker_base_path = os.path.join(base_path, "celery_broker") + os.makedirs(broker_base_path, exist_ok=True) broker_url = f"filesystem://{broker_base_path}" self.app = Celery(name, broker=broker_url, backend=backend_url) From 3bfc23631dc57d19af14dc021effa588a9775767 Mon Sep 17 00:00:00 2001 From: Andy Kuny Date: Wed, 24 Jan 2024 13:07:08 -0500 Subject: [PATCH 06/10] specify env in CI action --- .github/workflows/ci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 27859fc..bf061a5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,6 +10,8 @@ on: jobs: lint-and-test: runs-on: ubuntu-latest + env: + APP_ENV: "test" steps: - name: Check out repository From 8b9f50b4b4c23b386b9aac444a0d92b7ff69e87d Mon Sep 17 00:00:00 2001 From: Andy Kuny Date: Wed, 24 Jan 2024 13:15:16 -0500 Subject: [PATCH 07/10] Specify test env variables in pyproject.toml --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4152212..e0254cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,9 @@ test = "pytest:main" [tool.pytest.ini_options] env = [ "APP_ENV=test", - "DATABASE_URL=sqlite:///:memory:" + "DATABASE_URL=sqlite:///:memory:", + "QUEUE_BROKER_URL=nad_ch/infrastructure/celery_broker", + "QUEUE_BACKEND_URL=sqlite:///:memory:" ] [tool.flake8] From 6def4e5dc3ff94db482f76d26a6cc97d5b9e616e Mon Sep 17 00:00:00 2001 From: Andy Kuny Date: Wed, 24 Jan 2024 13:25:12 -0500 Subject: [PATCH 08/10] Update env naming convention --- .github/workflows/ci.yaml | 2 -- manifest-dev.yml | 2 +- nad_ch/application_context.py | 2 +- nad_ch/config/__init__.py | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bf061a5..27859fc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,8 +10,6 @@ on: jobs: lint-and-test: runs-on: ubuntu-latest - env: - APP_ENV: "test" steps: - name: Check out repository diff --git a/manifest-dev.yml b/manifest-dev.yml index 99b7cd4..f8ddcb2 100644 --- a/manifest-dev.yml +++ b/manifest-dev.yml @@ -12,4 +12,4 @@ applications: stack: cflinuxfs4 command: poetry run start web env: - APP_ENV: development-remote + APP_ENV: development_remote diff --git a/nad_ch/application_context.py b/nad_ch/application_context.py index 31ff34e..86ec1ec 100644 --- a/nad_ch/application_context.py +++ b/nad_ch/application_context.py @@ -108,7 +108,7 @@ def create_task_queue(self): def create_app_context(): if config.APP_ENV == "test": return TestApplicationContext() - elif config.APP_ENV == "development-local": + elif config.APP_ENV == "development_local": return DevLocalApplicationContext() else: return ApplicationContext() diff --git a/nad_ch/config/__init__.py b/nad_ch/config/__init__.py index 98a627a..62cdd9d 100644 --- a/nad_ch/config/__init__.py +++ b/nad_ch/config/__init__.py @@ -1,7 +1,7 @@ from .base import APP_ENV -if APP_ENV == 'development-local' or APP_ENV == 'test': +if APP_ENV == 'development_local' or APP_ENV == 'test': from .development_local import * -elif APP_ENV == 'development-remote': +elif APP_ENV == 'development_remote': from .development_remote import * From e88ae720ccea75f527b632fc008c7c9b68858456 Mon Sep 17 00:00:00 2001 From: Andy Kuny Date: Wed, 24 Jan 2024 13:34:34 -0500 Subject: [PATCH 09/10] Tweak env naming convention --- manifest-dev.yml | 2 +- nad_ch/application_context.py | 2 +- nad_ch/config/__init__.py | 4 ++-- scripts/init_dev_db.py | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/manifest-dev.yml b/manifest-dev.yml index f8ddcb2..749d0e7 100644 --- a/manifest-dev.yml +++ b/manifest-dev.yml @@ -12,4 +12,4 @@ applications: stack: cflinuxfs4 command: poetry run start web env: - APP_ENV: development_remote + APP_ENV: dev_remote diff --git a/nad_ch/application_context.py b/nad_ch/application_context.py index 86ec1ec..e60c4ed 100644 --- a/nad_ch/application_context.py +++ b/nad_ch/application_context.py @@ -108,7 +108,7 @@ def create_task_queue(self): def create_app_context(): if config.APP_ENV == "test": return TestApplicationContext() - elif config.APP_ENV == "development_local": + elif config.APP_ENV == "dev_local": return DevLocalApplicationContext() else: return ApplicationContext() diff --git a/nad_ch/config/__init__.py b/nad_ch/config/__init__.py index 62cdd9d..5c60543 100644 --- a/nad_ch/config/__init__.py +++ b/nad_ch/config/__init__.py @@ -1,7 +1,7 @@ from .base import APP_ENV -if APP_ENV == 'development_local' or APP_ENV == 'test': +if APP_ENV == 'dev_local' or APP_ENV == 'test': from .development_local import * -elif APP_ENV == 'development_remote': +elif APP_ENV == 'dev_remote': from .development_remote import * diff --git a/scripts/init_dev_db.py b/scripts/init_dev_db.py index 7ce65ef..1c19411 100644 --- a/scripts/init_dev_db.py +++ b/scripts/init_dev_db.py @@ -1,14 +1,14 @@ import os from sqlalchemy import create_engine from nad_ch.infrastructure.database import ModelBase -from nad_ch.config import DATABASE_URL_LOCAL +from nad_ch.config import DATABASE_URL def main(): - engine = create_engine(DATABASE_URL_LOCAL) + engine = create_engine(DATABASE_URL) # Check if the database file already exists - if os.path.exists(DATABASE_URL_LOCAL.split("///")[1]): + if os.path.exists(DATABASE_URL.split("///")[1]): print("Database already exists.") else: # Create all tables From ed02889568cd0f051b1d6f261b9d3e26cfd0dca2 Mon Sep 17 00:00:00 2001 From: Andy Kuny Date: Wed, 24 Jan 2024 13:59:51 -0500 Subject: [PATCH 10/10] Only deploy to dev_remote when the workflow is manually triggered --- .github/workflows/deploy-dev.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index fdf5634..e22a5bf 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -1,9 +1,6 @@ name: Deploy Dev on: - push: - branches: - - main workflow_dispatch: jobs: