From 04476af1899861848098c5825dcfb60643001c56 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 13:08:26 -0700 Subject: [PATCH] 21464 - Merge EFT into main branch (#1552) * 19910 - Python 3.12.2 upgrade (#1435) * Update docker files to python 3.12.2 * Update CI flows * update requirements * Update grcpiostatus and protobuf * Add python 3.12 to test CI * Add in more python 3.12 * Fix flake8 * Get the bulk of the unit tests working, upgrade marshmellow, upgrade flask, upgrade sqlalchemy - still need to fix "tracing", still need to fix sql-continuum, still need to fix exception handling, still need to fix JWT library flask-oidc-jwt (doesn't work with Flask 3.0 without update) * Small cleanup * remove autoflush param, didn't mean for it to be in there * Update requirements to pull from github, where we fixed the library * Clear out some old TODOs * clear out old todo * remove todo * old todo * todo removal * Remove todo * change default database name * Use more modern database * nuke tracer for now * add in ci for queue_python_upgrade * Fix linting * remove setup.cfg changes * fix unit test * Small unit test fix * use utcnow instead of now. * Move created on to utcnow * unit test fixes * Small unit test tweak * use datetime.now(tz=timezone.utc) * up google-auth version * Update prod.txt * Update requirements.txt * Update prod.txt * fix migration bug (#1440) * Changes to upgrade to Python 3.12 * lint fix * Small unit test fixes * Fix pay-admin * Update ftp-poller's requirements * 19939 - EFT - Invoice Reference (#1436) * Squashed commit of the following: commit e1c58f92eb12e9a6c4644f89279c1f877ca83a35 Author: Rodrigo Barraza Date: Tue Mar 5 08:42:01 2024 -0800 Cleanup auto_save commit 9b8a9a05a4ca4baa5716485885f56abcb988f48e Author: Rodrigo Barraza Date: Tue Mar 5 08:33:27 2024 -0800 Remove committing commit a05e210e2af0a72bd397721ce07c2bbabbd1f0c2 Author: Rodrigo Barraza Date: Tue Mar 5 08:30:07 2024 -0800 Reference and Receipt creation commit edb6e51edf1ff24b4e1e263484a474cfef440aff Author: Rodrigo Barraza Date: Tue Mar 5 08:18:47 2024 -0800 Updates to EFT Service Handling invoice reference creation * Reverting reconciliation changes * PR feedback updates * 19939 - EFT Reconciliation - Invoice Reference (#1437) * Updates to EFT Service Handling invoice reference creation * Reference and Receipt creation * Remove committing * Cleanup auto_save * Revert EFT service * PR feedback updates Bringing back auto_save * 19724 - EFT - Create CFS account job modification (#1441) * update api and payment-jobs for EFT cfs_account create * add updated requirements.txt from PR#18263 * isort fix * 18263 - New queues use PUB/SUB (#1438) * Update requirements.txt * Fix CI * Changes to convert project to new queues * Remove nats from workflows * remove nats from docker compose * remove q_cli * Remove more NATS * Remove more nats * Gun events-listener, combining the queues for less tech debt * More nats removal * Move payment-reconciliations into pay-queue * Change project structure of queues * Add in tests from events listener * Getting rid of some infrastructure and tech debt we don't need * Enhance gcp_queue_publisher * Wire up ftp poller to new queues * Add in enum, change util func * Small tweaks for gcp_queue_publisher * Put in adhoc test for publishing to queue * Add in PaymentToken dataclass shared across apps * shorten enums * Replace queue messages in mailer in payment-jobs * Add more MessageTypes to enum * More enum * Convert to PUB * missing comma * Add in pub * Rework entire PUB * Clean up clean up everybody clean up! * Small fix for ftp-poller * Fix ref * Holy moly third try it must be friday * fix lint * remove unused receipt method * payment jobs updates, makefile for pay-queue * linting fixes, requirements fixes * Dependencies are being funny with this one - will need to relook at it. This should work for now. * Update old references * Add in pay jobs CI * directory change * Use pip 24.0.0 * Flask-Migrate upgrade + disable compare_type * Small tweaks for jobs, still some failing tests * Use scalars to fix where conditions when there are tuples, as those cause exceptions * Fix CI * Fix unit test for pay-api * Lint fixes and others * Update CI * fix deps * update reqs * Take out tracing for BCOL-API * more tracing removal * 1 more spot for tracing * Fix linting, move pubsub to local, had issues referencing it as a library * Update requirements * More test / lint fixes * Cleanup conftest * more cleanup * Some unit tests working, more to go * More cleanup * 4 unit tests to go * Fix eft reconciliation tests * Lint fixes more unit test fixes * Unit tests should be all passing * Remove redundant comment * Cleanup * Lint fix * Add in code coverage for pay-queue * Put in event listener topic * Some cleanup * Update requirements * 19724_2 - EFT - Create CFS account job (#1442) * update api and payment-jobs for EFT cfs_account create * add updated requirements.txt from PR#18263 * isort fix * EFT - Create CFS account job - 2 * remove redundant function * remove incorrect comment * Pay API CD upgrade. * Rollback the auto formatting. * Fixed flake8. * 20299 - Poetry upgrade (#1443) * Migrate to Poetry * More cleanup, add dev deps * Init poetry, add in deps * Docker file poetry for bcol-api * Revert makefile changes * Fix make files * Docker file + Makefile update * makefile update * Makefile update * Fix makefiles * Fix pay-admin docker * update pay queue dockerfile * minor nudge * one more nudge * Fix missing port * Fix poetry for docker * attempt to fix CI * attempt to fix pay-queue migrations * attempt to fix CI #999 * Update paths * Fix pay queues migration path * Fix path for jobs * rename payment-jobs -> jobs * Update maintainer * CI fix * Add pay api gcp CD flow. (#1446) (#1447) Co-authored-by: Patrick Wei <44277752+pwei1018@users.noreply.github.com> * First run at pubsub emulation for integration tests + pg8000 upgrade (#1448) * First run at pubsub emulation * Use pg8000 instead of psycopg2 * Remove requirement of keys allow for unauthenticated client * Change gunicorn to match lear * Remove prehooks, no concepts of prehooks anymore * Remove jaeger-client * Fix unit tests * Update dependencies * Use test environment variables * Update secret for linux * Checkout v4 * Add in extra hosts * bind to 0.0.0.0 * Upgrade CD flows for Payment projects. (#1449) * Upgrade CD flows for Payment projects. * fixed linting. * Fixed empty value issue. * Fixed deployment config issue. * Fixed deployment environment variables issue. * Fixed the configuration. * Fixed CD issues. (#1452) * Fixed empty value issue. * Fixed deployment config issue. * Fixed deployment environment variables issue. * Fixed the configuration. * Fixed missing service account. * Fixed deployment config issue. * Fixed deployment environment variables issue. * Fixed missing service account. * 19875 - PAY Jobs - Disbursement Process handle Partial Refunds (#1428) * Fixed typo. * 14521 - Get rid of sqlalchemy-continuum (#1450) * Use new versioning library thanks to Thor/ people working on LEAR. Disable activity log for now, we'll add audit columns and just use the history tables. Still need a migration to move data from verisons -> history tables. * Add in version column * Add in default for version * Small nudge * Another tweak refunds_partial table missing * Migration fixes along the way * Migration query * Easier for copy and paste * Credit for script * Fix multiple head issue * Small tweaks, implement payment_methods again * Fix warnings + depreciated cls.query.get() * remove pytest-asyncio * Implement versioning with a unit test * Remove unused statement, lint cleanup * Change commit -> flush * Add in some checks to see if column has changed before updating it * Rollback conditional checks. This also reverts commit 1e84bec8bb11f939cd330368c371768fdeb42fdf. * Minor touch ups. * 19936 - EFT - PAY-API changes to handle CFS when switching payment methods (#1445) * add eft cfs_account create to pay-api * add unit test for eft payment account switch * pylint fix * lint fix * update eft cfs account creation * Add in logging * move Migrate into __init__.py instead of in wsgi.py * move out check for migration mode * Enhance logging * Small touch ups to get it to work in docker and hopefully gcp * 19721 - Updating EFT service for EFT invoice task (#1455) * Update EFT service * Expose EFT Service * Update logging * Enum support for invoice tasks (#1457) * 19721- Create EFT Invoices task (#1456) * Create EFT Invoices task * Linting * Remove comments, update commit hash * Adding enums, code feedback * timezone fix * Refactoring into smaller functions * enum support * Poetry lock update * Linting * pylint cleanup * Delete enums.py * Increased tests and final fixes * Remove print * Sync from main (#1460) * Add pay api gcp CD flow. (#1446) * Bump pillow from 10.2.0 to 10.3.0 in /report-api (#1458) Bumps [pillow](https://github.com/python-pillow/Pillow) from 10.2.0 to 10.3.0. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](https://github.com/python-pillow/Pillow/compare/10.2.0...10.3.0) --- updated-dependencies: - dependency-name: pillow dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add in TZ conversion (#1459) --------- Signed-off-by: dependabot[bot] Co-authored-by: Patrick Wei <44277752+pwei1018@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * add verification logic in some .setters to avoid unnecessary db actions (#1462) * 19723 - Exposing eft short names service (#1466) * Include EFTShortNames in service * as EftShortNamesService * Lint fix * 19723 - Linking EFT Job (#1464) * Linking EFTs * Minor cleanup * Tests * Remove print * 20457 - SBC-PAY Common Queue (#1467) * Pub/Sub Integration * fix unit test * unit test fix * queue name change * lint fix * lint fix * unit test fix * pay queue unit test fix * 19496 refactor eft shortnames to support multiple account linking and states (#1488) * 19496 -EFT TDI17 Processing / Multi Account linking (#1489) * 19496 - EFT TDI17 processing update, Multi account linking fixes * update queue poetry.lock * linting * 20454 - Pay-queue JWT Verification (#1491) * 20454 - Fix a jwt condition check issue (#1492) * just for test * pubsub jwt verification * remove extra code * add back DISABLE_PAD_SUCCESS_EMAIL, it deleted by incidence * 20454 - Add test logs (#1493) * 20414 - EFT Short name summaries search (#1494) * 20414 - EFT Short name summaries search * PR feedback * 19722 - Unlinking EFT Task (#1487) * Unlinking EFT * Flake fixes * Unlinking EFT test * Cleanup and fixes * Fixing tests * Updates and fixes * renaming function * Lint fixes * Flake8 fix * Test fix * 20420 - EFT Short name search (#1496) * 20420 - EFT Short name search - refactor for EFT Enabled Accounts Table search - clean up params / schema * version increment * clean up statement filter * 20421 - Add cfs account status to search eft names (#1525) * 20417 - EFT Shortname links / transaction search (#1526) * 20417 - EFT Shortname links / transaction search * fixes after doing some linking tests with the UI * Fix CI * Fixes, PR Feedback --------- Co-authored-by: Travis Semple * 20419 - Short name linking status (#1540) - Handle basic unlinking for short name still in pending - return whether associated account link has pending payments * Merge branch 'main' of https://github.com/bcgov/sbc-pay into feature-queue-python-upgrade * Fix linting issues. Make units tests pass for pay-api. * Fix lint and test for ftp-poller * pay-queue lint, ci passing * update sbc-common-components, also remove because pay-api already includes it. * remove requirements.txt for pay-admin * poetry update pay-api * Fix lint plus unit tests for payment-jobs * 21464 - Resync EFT branch with main (#1547) * Merge branch 'main' of https://github.com/bcgov/sbc-pay into feature-queue-python-upgrade * Fix linting issues. Make units tests pass for pay-api. * Fix lint and test for ftp-poller * pay-queue lint, ci passing * update sbc-common-components, also remove because pay-api already includes it. * remove requirements.txt for pay-admin * poetry update pay-api * Fix lint plus unit tests for payment-jobs * Revert "21464 - Resync EFT branch with main (#1547)" (#1550) This reverts commit 23fed0f2445acf1c3fddbb6e412ad5ea92f4858a. * Lint fixes * Small lint fixes * Remove duplicate literal * fix hint * remove redundant return * Codecov fixes * more lint fixes * Fix CD * Put back pay-api cd * update poetry, will fix error building * update psycopg2-binary * fix CD * remove requirements.txt * use latest poetry * remove more requirements * Put back gunicorn config, comment for GCP. --------- Signed-off-by: dependabot[bot] Co-authored-by: Jia Xu Co-authored-by: Rodrigo Barraza Co-authored-by: pwei1018 Co-authored-by: Patrick Wei <44277752+pwei1018@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Odysseus Chiu --- .github/workflows/bcol-api-cd.yml | 4 +- .github/workflows/bcol-api-ci.yml | 19 +- .github/workflows/ftp-poller-cd.yml | 4 +- .github/workflows/ftp-poller-ci.yml | 23 +- .github/workflows/notebook-report-cd.yml | 4 +- .github/workflows/notebook-report-ci.yml | 18 +- .github/workflows/pay-admin-cd-gcp.yml | 30 + .github/workflows/pay-admin-ci.yml | 25 +- .github/workflows/pay-api-cd-gcp.yml | 6 +- .github/workflows/pay-api-ci.yml | 27 +- .github/workflows/pay-queue-ci.yml | 25 +- .github/workflows/pay-queue-gcp-cd.yml | 32 + .github/workflows/payment-jobs-cd-gcp.yml | 32 + .github/workflows/payment-jobs-ci.yml | 23 +- .github/workflows/ppr-report-api-cd.yml | 4 +- .github/workflows/report-api-cd.yml | 4 +- .github/workflows/report-api-ci.yml | 18 +- .gitignore | 6 +- bcol-api/Dockerfile | 83 +- bcol-api/Makefile | 26 +- bcol-api/docker-entrypoint.sh | 4 - bcol-api/poetry.lock | 1785 +++++++++++ bcol-api/pyproject.toml | 55 + bcol-api/requirements.txt | 51 - bcol-api/requirements/dev.txt | 25 - bcol-api/requirements/prod.txt | 19 - bcol-api/requirements/repo-libraries.txt | 1 - bcol-api/setup.cfg | 2 +- bcol-api/src/bcol_api/resources/__init__.py | 1 - .../src/bcol_api/resources/bcol_payment.py | 2 - .../src/bcol_api/resources/bcol_profile.py | 3 - bcol-api/src/bcol_api/utils/logging.py | 7 +- bcol-api/src/bcol_api/utils/trace.py | 8 - bcol-api/tests/docker/docker-compose.yml | 2 +- .../tests/unit/services/test_bcol_payment.py | 7 +- codecov.yaml | 11 +- jobs/ftp-poller/Dockerfile | 61 +- jobs/ftp-poller/Makefile | 32 +- jobs/ftp-poller/README.md | 2 +- jobs/ftp-poller/invoke_jobs.py | 3 +- .../openshift/ftp-poller-build.json | 139 - .../openshift/ftp-poller-deploy.json | 417 --- jobs/ftp-poller/poetry.lock | 2697 ++++++++++++++++ jobs/ftp-poller/pyproject.toml | 51 + jobs/ftp-poller/requirements.txt | 50 - jobs/ftp-poller/requirements/dev.txt | 27 - jobs/ftp-poller/requirements/prod.txt | 15 - .../requirements/repo-libraries.txt | 3 - jobs/ftp-poller/setup.cfg | 2 +- jobs/ftp-poller/tests/jobs/conftest.py | 78 +- jobs/ftp-poller/tests/jobs/test_sftp.py | 19 +- jobs/ftp-poller/utils/logger.py | 11 +- jobs/notebook-report/Dockerfile | 2 +- jobs/notebook-report/notebookreport.py | 3 + jobs/notebook-report/requirements.txt | 2 +- jobs/payment-jobs/Dockerfile | 58 +- jobs/payment-jobs/Makefile | 29 +- jobs/payment-jobs/config.py | 39 +- .../devops/gcp/clouddeploy-targets.yaml | 94 + jobs/payment-jobs/devops/vaults.gcp.env | 88 + jobs/payment-jobs/invoke_jobs.py | 14 +- .../openshift/payment-job-build.json | 139 - .../openshift/payment-job-deploy.json | 417 --- jobs/payment-jobs/poetry.lock | 2747 +++++++++++++++++ jobs/payment-jobs/pyproject.toml | 58 + jobs/payment-jobs/requirements.txt | 94 - .../requirements/bcregistry-libraries.txt | 3 - jobs/payment-jobs/requirements/dev.txt | 28 - jobs/payment-jobs/requirements/prod.txt | 20 - jobs/payment-jobs/services/oracle.py | 8 +- jobs/payment-jobs/setup.cfg | 2 +- jobs/payment-jobs/tasks/ap_task.py | 11 +- .../tasks/cfs_create_account_task.py | 15 +- .../tasks/cfs_create_invoice_task.py | 142 +- jobs/payment-jobs/tasks/eft_transfer_task.py | 289 -- .../tasks/ejv_partner_distribution_task.py | 135 +- jobs/payment-jobs/tasks/ejv_payment_task.py | 21 +- .../tasks/electronic_funds_transfer_task.py | 281 ++ .../tasks/statement_notification_task.py | 2 +- jobs/payment-jobs/tests/docker/nginx.conf | 2 +- jobs/payment-jobs/tests/jobs/conftest.py | 164 +- jobs/payment-jobs/tests/jobs/factory.py | 96 +- .../jobs/test_cfs_create_account_task.py | 13 +- .../jobs/test_cfs_create_invoice_task.py | 131 +- .../tests/jobs/test_eft_transfer_task.py | 178 -- .../test_ejv_partner_distribution_task.py | 4 +- ...artner_partial_refund_distribution_task.py | 86 + .../tests/jobs/test_ejv_payment_task.py | 11 +- .../test_electronic_funds_transfer_task.py | 131 + .../tests/jobs/test_generate_statements.py | 6 +- .../tests/jobs/test_statement_due_task.py | 67 +- .../jobs/test_statement_notification_task.py | 393 ++- jobs/payment-jobs/utils/logger.py | 7 +- jobs/payment-jobs/utils/mailer.py | 2 - pay-admin/Dockerfile | 83 +- pay-admin/Makefile | 26 +- pay-admin/admin/__init__.py | 2 +- pay-admin/admin/config.py | 14 +- pay-admin/devops/gcp/clouddeploy-targets.yaml | 104 + pay-admin/devops/vaults.gcp.env | 7 + pay-admin/devops/vaults.json | 15 - .../openshift/templates/pay-admin-build.json | 111 - .../openshift/templates/pay-admin-deploy.json | 316 -- pay-admin/poetry.lock | 2473 +++++++++++++++ pay-admin/pyproject.toml | 55 + pay-admin/requirements.txt | 30 - pay-admin/requirements/dev.txt | 24 - pay-admin/requirements/prod.txt | 15 - pay-admin/requirements/repo-libraries.txt | 3 - pay-admin/secrets/keycloak.json | 9 +- pay-admin/setup.cfg | 2 +- pay-admin/tests/conftest.py | 29 +- pay-api/Dockerfile | 83 +- pay-api/Makefile | 32 +- pay-api/devops/gcp/clouddeploy-targets.yaml | 104 + pay-api/devops/vaults.gcp.env | 59 + pay-api/docker-entrypoint.sh | 4 - pay-api/docs/dotenv_template | 2 +- pay-api/gunicorn_config.py | 1 + pay-api/manage.py | 7 +- pay-api/migrations/README.md | 2 +- pay-api/migrations/env.py | 1 - ...caed21_populate_receipt_number_payments.py | 7 +- .../1283463d3b4a_new_payment_method_tables.py | 4 +- ..._update_refund_payment_date_to_invoices.py | 13 +- .../2023_09_28_456234145e5e_eft_processing.py | 1 - ...024_03_06_04b8a7bed74e_ejv_link_generic.py | 39 + ..._03_15_fb3ba97b603a_new_version_tables_.py | 241 ++ ...ed2340a43c_move_from_version_to_history.py | 359 +++ ...67cf1bd9e_19496_eft_short_names_updates.py | 123 + ...d37727a5_20417_eft_credit_invoice_links.py | 35 + ...fix_payment_account_id_in_payment_table.py | 4 +- ...3_ppr_and_dissolution_filing_type_codes.py | 10 +- .../4cc39da0bee7_payment_system_account.py | 131 - .../56c5db141909_refactor_payment_account.py | 6 +- .../5d9997f7e649_variable_fee_code.py | 6 +- .../migrations/versions/609b98d87a72_rpt.py | 6 +- .../migrations/versions/643790dd3334_cso.py | 4 +- pay-api/migrations/versions/6a6b042b831a_.py | 8 +- .../6f0fe9f23d8c_distribution_code_changes.py | 12 +- .../7c19ee3a58aa_invoice_amount_to_payment.py | 4 +- .../8652bf9c03ff_payment_to_invoice.py | 4 +- ...44955fb6_add_created_user_display_names.py | 4 +- .../a11be9fe1a6a_distribution_code.py | 6 +- .../b336780735dc_registration_filing_type.py | 6 +- .../b6e28faea978_rename_tables_to_plurals.py | 14 +- .../versions/b7443d501d98_entity_fee_codes.py | 4 +- .../c67213f860ea_incorporation_fee_codes.py | 45 +- .../versions/c871202927f0_rush_fee_code.py | 6 +- .../versions/dbe9dc38ac33_firms_fee_codes.py | 6 +- pay-api/migrations/versions/e748b5c19247_.py | 10 +- pay-api/openshift/templates/dc.json | 368 --- .../openshift/templates/pay-api-build.json | 111 - .../openshift/templates/pay-api-deploy.json | 360 --- pay-api/poetry.lock | 2366 ++++++++++++++ pay-api/pre-hook-update-db.sh | 4 - pay-api/pre_hook_create_database.py | 40 - pay-api/pyproject.toml | 107 + pay-api/requirements.txt | 69 - pay-api/requirements/dev.txt | 31 - pay-api/requirements/prod.txt | 31 - pay-api/requirements/repo-libraries.txt | 2 - pay-api/setup.cfg | 6 +- pay-api/src/pay_api/__init__.py | 67 +- pay-api/src/pay_api/config.py | 103 +- .../pay_api/factory/payment_system_factory.py | 3 +- pay-api/src/pay_api/logging.conf | 8 +- pay-api/src/pay_api/models/__init__.py | 5 +- pay-api/src/pay_api/models/account_fee.py | 5 +- pay-api/src/pay_api/models/base_model.py | 45 +- pay-api/src/pay_api/models/base_schema.py | 3 +- pay-api/src/pay_api/models/cfs_account.py | 5 +- .../pay_api/models/cfs_account_status_code.py | 3 +- pay-api/src/pay_api/models/corp_type.py | 3 +- pay-api/src/pay_api/models/credit.py | 3 +- pay-api/src/pay_api/models/custom_query.py | 5 +- pay-api/src/pay_api/models/db.py | 9 +- .../models/disbursement_status_code.py | 3 +- .../src/pay_api/models/distribution_code.py | 7 +- pay-api/src/pay_api/models/eft_credit.py | 8 - .../pay_api/models/eft_credit_invoice_link.py | 6 +- .../pay_api/models/eft_process_status_code.py | 3 +- .../pay_api/models/eft_short_name_links.py | 104 + pay-api/src/pay_api/models/eft_short_names.py | 71 +- pay-api/src/pay_api/models/eft_transaction.py | 24 +- .../{ejv_invoice_link.py => ejv_link.py} | 17 +- pay-api/src/pay_api/models/error_code.py | 3 +- pay-api/src/pay_api/models/fee_code.py | 3 +- pay-api/src/pay_api/models/fee_schedule.py | 5 +- pay-api/src/pay_api/models/filing_type.py | 3 +- pay-api/src/pay_api/models/invoice.py | 8 +- .../models/invoice_reference_status_code.py | 3 +- .../src/pay_api/models/invoice_status_code.py | 3 +- .../pay_api/models/line_item_status_code.py | 3 +- .../models/notification_status_code.py | 3 +- pay-api/src/pay_api/models/payment.py | 13 +- pay-api/src/pay_api/models/payment_account.py | 13 +- .../src/pay_api/models/payment_line_item.py | 3 +- pay-api/src/pay_api/models/payment_method.py | 3 +- .../src/pay_api/models/payment_status_code.py | 3 +- pay-api/src/pay_api/models/payment_system.py | 3 +- pay-api/src/pay_api/models/receipt.py | 3 +- pay-api/src/pay_api/models/refund.py | 3 +- pay-api/src/pay_api/models/refunds_partial.py | 15 +- pay-api/src/pay_api/models/routing_slip.py | 4 +- .../models/routing_slip_status_code.py | 3 +- pay-api/src/pay_api/models/statement.py | 91 +- .../src/pay_api/models/statement_invoices.py | 3 +- .../pay_api/models/statement_recipients.py | 5 +- .../src/pay_api/models/statement_settings.py | 3 +- .../pay_api/models/transaction_status_code.py | 3 +- pay-api/src/pay_api/resources/ops.py | 2 +- pay-api/src/pay_api/resources/v1/account.py | 7 - .../resources/v1/account_statements.py | 4 - .../v1/account_statements_notifications.py | 3 - .../v1/account_statements_settings.py | 3 - pay-api/src/pay_api/resources/v1/code.py | 3 - .../src/pay_api/resources/v1/distributions.py | 7 - .../pay_api/resources/v1/eft_short_names.py | 145 +- .../pay_api/resources/v1/fas/routing_slip.py | 10 - pay-api/src/pay_api/resources/v1/fee.py | 2 - .../src/pay_api/resources/v1/fee_schedule.py | 2 - pay-api/src/pay_api/resources/v1/invoice.py | 7 - pay-api/src/pay_api/resources/v1/invoices.py | 2 - .../resources/v1/non_sufficient_funds.py | 3 - pay-api/src/pay_api/resources/v1/payment.py | 3 - .../src/pay_api/resources/v1/transaction.py | 5 - pay-api/src/pay_api/services/__init__.py | 3 + .../src/pay_api/services/deposit_service.py | 10 +- .../src/pay_api/services/distribution_code.py | 35 +- pay-api/src/pay_api/services/eft_service.py | 53 +- .../services/eft_short_name_summaries.py | 141 + .../src/pay_api/services/eft_short_names.py | 456 ++- .../src/pay_api/services/eft_transactions.py | 100 +- .../src/pay_api/services/fas/routing_slip.py | 2 - pay-api/src/pay_api/services/fee_schedule.py | 7 +- .../src/pay_api/services/gcp_queue/logging.py | 67 + .../pay_api/services/internal_pay_service.py | 3 +- .../pay_api/services/non_sufficient_funds.py | 2 +- pay-api/src/pay_api/services/payment.py | 2 +- .../src/pay_api/services/payment_account.py | 45 +- pay-api/src/pay_api/services/statement.py | 5 +- pay-api/src/pay_api/utils/constants.py | 1 + pay-api/src/pay_api/utils/enums.py | 22 +- pay-api/src/pay_api/utils/errors.py | 1 + pay-api/src/pay_api/utils/logging.py | 7 +- pay-api/src/pay_api/utils/trace.py | 22 - pay-api/src/pay_api/utils/util.py | 29 +- pay-api/tests/conftest.py | 129 +- pay-api/tests/docker/nginx.conf | 2 +- pay-api/tests/unit/api/fas/test_refund.py | 6 +- .../tests/unit/api/fas/test_routing_slip.py | 60 +- pay-api/tests/unit/api/test_account.py | 15 + pay-api/tests/unit/api/test_cors_preflight.py | 12 +- .../tests/unit/api/test_eft_short_names.py | 704 +++-- .../tests/unit/api/test_eft_transactions.py | 176 +- pay-api/tests/unit/api/test_fee.py | 5 +- pay-api/tests/unit/api/test_ops.py | 11 +- .../tests/unit/api/test_payment_request.py | 6 +- pay-api/tests/unit/api/test_receipt.py | 9 +- .../models/test_eft_credit_invoice_link.py | 7 +- pay-api/tests/unit/models/test_eft_credits.py | 3 +- .../tests/unit/models/test_eft_gl_transfer.py | 3 +- .../unit/models/test_eft_short_name_links.py | 73 + .../tests/unit/models/test_eft_short_names.py | 20 +- .../unit/services/test_direct_pay_service.py | 10 +- .../unit/services/test_payment_transaction.py | 1 - pay-api/tests/unit/services/test_statement.py | 44 +- pay-api/tests/utilities/base_test.py | 61 +- pay-api/wsgi.py | 12 +- pay-queue/Dockerfile | 83 +- pay-queue/Makefile | 31 +- pay-queue/app.py | 0 pay-queue/devops/gcp/clouddeploy-targets.yaml | 104 + .../openshift/templates/pay-queue-build.json | 111 - .../openshift/templates/pay-queue-deploy.json | 200 -- pay-queue/poetry.lock | 2673 ++++++++++++++++ pay-queue/pyproject.toml | 53 + pay-queue/requirements.txt | 90 - .../requirements/bcregistry-libraries.txt | 3 - pay-queue/requirements/dev.txt | 31 - pay-queue/requirements/prod.txt | 15 - pay-queue/setup.cfg | 2 +- pay-queue/src/pay_queue/config.py | 9 +- pay-queue/src/pay_queue/logging.conf | 34 - .../pay_queue/services/cgi_reconciliations.py | 29 +- .../services/eft/eft_reconciliation.py | 59 +- .../src/pay_queue/services/eft/eft_record.py | 22 +- pay-queue/src/pay_queue/version.py | 2 +- pay-queue/tests/conftest.py | 224 +- .../integration/test_cgi_reconciliations.py | 61 +- .../integration/test_eft_reconciliation.py | 308 +- pay-queue/tests/unit/test_eft_file_parser.py | 5 + pay-queue/tests/utilities/factory_utils.py | 3 +- report-api/Dockerfile | 26 +- report-api/src/api/resources/__init__.py | 1 - report-api/src/api/services/report_service.py | 1 + report-api/src/api/utils/logging.py | 11 +- 298 files changed, 21150 insertions(+), 6607 deletions(-) create mode 100644 .github/workflows/pay-admin-cd-gcp.yml create mode 100644 .github/workflows/pay-queue-gcp-cd.yml create mode 100644 .github/workflows/payment-jobs-cd-gcp.yml delete mode 100755 bcol-api/docker-entrypoint.sh create mode 100644 bcol-api/poetry.lock create mode 100644 bcol-api/pyproject.toml delete mode 100644 bcol-api/requirements.txt delete mode 100755 bcol-api/requirements/dev.txt delete mode 100755 bcol-api/requirements/prod.txt delete mode 100644 bcol-api/requirements/repo-libraries.txt delete mode 100644 jobs/ftp-poller/openshift/ftp-poller-build.json delete mode 100644 jobs/ftp-poller/openshift/ftp-poller-deploy.json create mode 100644 jobs/ftp-poller/poetry.lock create mode 100644 jobs/ftp-poller/pyproject.toml delete mode 100644 jobs/ftp-poller/requirements.txt delete mode 100644 jobs/ftp-poller/requirements/dev.txt delete mode 100644 jobs/ftp-poller/requirements/prod.txt delete mode 100644 jobs/ftp-poller/requirements/repo-libraries.txt create mode 100644 jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml create mode 100644 jobs/payment-jobs/devops/vaults.gcp.env delete mode 100644 jobs/payment-jobs/openshift/payment-job-build.json delete mode 100644 jobs/payment-jobs/openshift/payment-job-deploy.json create mode 100644 jobs/payment-jobs/poetry.lock create mode 100644 jobs/payment-jobs/pyproject.toml delete mode 100644 jobs/payment-jobs/requirements.txt delete mode 100644 jobs/payment-jobs/requirements/bcregistry-libraries.txt delete mode 100644 jobs/payment-jobs/requirements/dev.txt delete mode 100644 jobs/payment-jobs/requirements/prod.txt delete mode 100644 jobs/payment-jobs/tasks/eft_transfer_task.py create mode 100644 jobs/payment-jobs/tasks/electronic_funds_transfer_task.py delete mode 100644 jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py create mode 100644 jobs/payment-jobs/tests/jobs/test_ejv_partner_partial_refund_distribution_task.py create mode 100644 jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py create mode 100644 pay-admin/devops/gcp/clouddeploy-targets.yaml create mode 100644 pay-admin/devops/vaults.gcp.env delete mode 100644 pay-admin/devops/vaults.json delete mode 100644 pay-admin/openshift/templates/pay-admin-build.json delete mode 100644 pay-admin/openshift/templates/pay-admin-deploy.json create mode 100644 pay-admin/poetry.lock create mode 100644 pay-admin/pyproject.toml delete mode 100644 pay-admin/requirements.txt delete mode 100644 pay-admin/requirements/dev.txt delete mode 100644 pay-admin/requirements/prod.txt delete mode 100644 pay-admin/requirements/repo-libraries.txt create mode 100644 pay-api/devops/gcp/clouddeploy-targets.yaml create mode 100644 pay-api/devops/vaults.gcp.env delete mode 100755 pay-api/docker-entrypoint.sh create mode 100644 pay-api/migrations/versions/2024_03_06_04b8a7bed74e_ejv_link_generic.py create mode 100644 pay-api/migrations/versions/2024_03_15_fb3ba97b603a_new_version_tables_.py create mode 100644 pay-api/migrations/versions/2024_03_18_52ed2340a43c_move_from_version_to_history.py create mode 100644 pay-api/migrations/versions/2024_04_12_29867cf1bd9e_19496_eft_short_names_updates.py create mode 100644 pay-api/migrations/versions/2024_05_07_4b3bd37727a5_20417_eft_credit_invoice_links.py delete mode 100644 pay-api/openshift/templates/dc.json delete mode 100644 pay-api/openshift/templates/pay-api-build.json delete mode 100644 pay-api/openshift/templates/pay-api-deploy.json create mode 100644 pay-api/poetry.lock delete mode 100755 pay-api/pre-hook-update-db.sh delete mode 100644 pay-api/pre_hook_create_database.py create mode 100644 pay-api/pyproject.toml delete mode 100644 pay-api/requirements.txt delete mode 100755 pay-api/requirements/dev.txt delete mode 100644 pay-api/requirements/prod.txt delete mode 100644 pay-api/requirements/repo-libraries.txt create mode 100644 pay-api/src/pay_api/models/eft_short_name_links.py rename pay-api/src/pay_api/models/{ejv_invoice_link.py => ejv_link.py} (80%) create mode 100644 pay-api/src/pay_api/services/eft_short_name_summaries.py create mode 100644 pay-api/src/pay_api/services/gcp_queue/logging.py delete mode 100644 pay-api/src/pay_api/utils/trace.py create mode 100644 pay-api/tests/unit/models/test_eft_short_name_links.py mode change 100644 => 100755 pay-queue/app.py create mode 100644 pay-queue/devops/gcp/clouddeploy-targets.yaml delete mode 100755 pay-queue/openshift/templates/pay-queue-build.json delete mode 100755 pay-queue/openshift/templates/pay-queue-deploy.json create mode 100644 pay-queue/poetry.lock create mode 100644 pay-queue/pyproject.toml delete mode 100644 pay-queue/requirements.txt delete mode 100644 pay-queue/requirements/bcregistry-libraries.txt delete mode 100755 pay-queue/requirements/dev.txt delete mode 100644 pay-queue/requirements/prod.txt delete mode 100644 pay-queue/src/pay_queue/logging.conf diff --git a/.github/workflows/bcol-api-cd.yml b/.github/workflows/bcol-api-cd.yml index 4c99a3c15..ca8bc50c4 100644 --- a/.github/workflows/bcol-api-cd.yml +++ b/.github/workflows/bcol-api-cd.yml @@ -31,7 +31,7 @@ jobs: name: "dev" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login Openshift shell: bash @@ -74,7 +74,7 @@ jobs: name: "${{ github.event.inputs.environment }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env by input run: | echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV diff --git a/.github/workflows/bcol-api-ci.yml b/.github/workflows/bcol-api-ci.yml index 0ffb85211..8d4fe1ad5 100644 --- a/.github/workflows/bcol-api-ci.yml +++ b/.github/workflows/bcol-api-ci.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - feature-queue-python-upgrade paths: - "bcol-api/**" @@ -19,7 +20,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -28,10 +29,10 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -79,6 +80,10 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 @@ -92,7 +97,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -118,8 +123,12 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/ftp-poller-cd.yml b/.github/workflows/ftp-poller-cd.yml index 57ed7e787..f358a3732 100644 --- a/.github/workflows/ftp-poller-cd.yml +++ b/.github/workflows/ftp-poller-cd.yml @@ -31,7 +31,7 @@ jobs: name: "dev" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login Openshift shell: bash @@ -74,7 +74,7 @@ jobs: name: "${{ github.event.inputs.environment }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env by input run: | echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV diff --git a/.github/workflows/ftp-poller-ci.yml b/.github/workflows/ftp-poller-ci.yml index 6b611b114..4c856cf02 100644 --- a/.github/workflows/ftp-poller-ci.yml +++ b/.github/workflows/ftp-poller-ci.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - feature-queue-python-upgrade paths: - "jobs/ftp-poller/**" @@ -19,7 +20,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -28,10 +29,10 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -51,7 +52,7 @@ jobs: testing: needs: setup-job env: - DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/postgres" + DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/pay-test" USE_DOCKER_MOCK: "YES" JWT_OIDC_ISSUER: "http://localhost:8081/auth/realms/demo" SBC_AUTH_ADMIN_CLIENT_ID: "sbc-auth-admin" @@ -59,20 +60,24 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres + POSTGRES_DB: pay-test ports: - 5432:5432 # needed because the postgres container does not provide a healthcheck options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -96,8 +101,12 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/notebook-report-cd.yml b/.github/workflows/notebook-report-cd.yml index 020a5d8d2..7eea8db18 100644 --- a/.github/workflows/notebook-report-cd.yml +++ b/.github/workflows/notebook-report-cd.yml @@ -31,7 +31,7 @@ jobs: name: "dev" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login Openshift shell: bash @@ -69,7 +69,7 @@ jobs: name: "${{ github.event.inputs.environment }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env by input run: | echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV diff --git a/.github/workflows/notebook-report-ci.yml b/.github/workflows/notebook-report-ci.yml index 18314494f..96a2e68b9 100644 --- a/.github/workflows/notebook-report-ci.yml +++ b/.github/workflows/notebook-report-ci.yml @@ -19,7 +19,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -28,10 +28,10 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -55,6 +55,10 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 @@ -68,7 +72,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -92,8 +96,12 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/pay-admin-cd-gcp.yml b/.github/workflows/pay-admin-cd-gcp.yml new file mode 100644 index 000000000..484c0ae28 --- /dev/null +++ b/.github/workflows/pay-admin-cd-gcp.yml @@ -0,0 +1,30 @@ +name: Pay Admin CD GCP + +on: + push: + branches: + - feature-gcp + paths: + - "pay-admin/**" + workflow_dispatch: + inputs: + target: + description: "Deploy To" + required: true + type: choice + options: + - dev + - test + - sandbox + - prod + +jobs: + pay-admin-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "pay-admin" + working_directory: "./pay-admin" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} diff --git a/.github/workflows/pay-admin-ci.yml b/.github/workflows/pay-admin-ci.yml index 83c43fc57..1007a80eb 100644 --- a/.github/workflows/pay-admin-ci.yml +++ b/.github/workflows/pay-admin-ci.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - feature-queue-python-upgrade paths: - "pay-admin/**" - "pay-api/src/pay_api/models/**" @@ -20,7 +21,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -29,10 +30,10 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -53,25 +54,31 @@ jobs: needs: setup-job env: FLASK_ENV: "testing" - DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/postgres" + # Needs different database than POSTGRES otherwise dropping database doesn't work + DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/pay-test" runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres + # Needs different database than POSTGRES otherwise dropping database doesn't work + POSTGRES_DB: pay-test ports: - 5432:5432 # needed because the postgres container does not provide a healthcheck options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -95,8 +102,12 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/pay-api-cd-gcp.yml b/.github/workflows/pay-api-cd-gcp.yml index a5fe4c8dc..b33b75ff0 100644 --- a/.github/workflows/pay-api-cd-gcp.yml +++ b/.github/workflows/pay-api-cd-gcp.yml @@ -1,9 +1,9 @@ -name: Pay API CD +name: Pay API CD GCP on: push: branches: - - feature-queue-python-upgrade + - feature-gcp paths: - "pay-api/**" workflow_dispatch: @@ -27,4 +27,4 @@ jobs: working_directory: "./pay-api" secrets: WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} - GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} diff --git a/.github/workflows/pay-api-ci.yml b/.github/workflows/pay-api-ci.yml index 955760f4e..b2fa9295c 100644 --- a/.github/workflows/pay-api-ci.yml +++ b/.github/workflows/pay-api-ci.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - feature-queue-python-upgrade paths: - "pay-api/**" @@ -19,7 +20,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -28,10 +29,10 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -52,7 +53,8 @@ jobs: needs: setup-job env: FLASK_ENV: "testing" - DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/postgres" + # Needs different database than POSTGRES otherwise dropping database doesn't work + DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/pay-test" USE_TEST_KEYCLOAK_DOCKER: "YES" USE_DOCKER_MOCK: "YES" @@ -68,20 +70,25 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: - image: postgres:12 + image: postgres:15.6 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres + # Needs different database than POSTGRES otherwise dropping database doesn't work + POSTGRES_DB: pay-test ports: - 5432:5432 # needed because the postgres container does not provide a healthcheck options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -105,8 +112,12 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/pay-queue-ci.yml b/.github/workflows/pay-queue-ci.yml index 8d1908951..238da6343 100644 --- a/.github/workflows/pay-queue-ci.yml +++ b/.github/workflows/pay-queue-ci.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - feature-queue-python-upgrade paths: - "pay-queue/**" - "pay-api/src/pay_api/models/**" @@ -20,7 +21,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -29,10 +30,10 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -52,7 +53,7 @@ jobs: testing: needs: setup-job env: - DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/postgres" + DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/pay-test" USE_DOCKER_MOCK: "YES" MINIO_ENDPOINT: "localhost:9000" MINIO_ACCESS_KEY: "minio" @@ -67,20 +68,24 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: - image: postgres:12 + image: postgres:15 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres + POSTGRES_DB: pay-test ports: - 5432:5432 # needed because the postgres container does not provide a healthcheck options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -104,8 +109,12 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/pay-queue-gcp-cd.yml b/.github/workflows/pay-queue-gcp-cd.yml new file mode 100644 index 000000000..b66871c61 --- /dev/null +++ b/.github/workflows/pay-queue-gcp-cd.yml @@ -0,0 +1,32 @@ +name: Pay Queue CD GCP + +on: + push: + branches: + - feature-gcp + paths: + - "pay-queue/**" + - "pay-api/src/pay_api/models/**" + - "pay-api/src/pay_api/services/cfs_service.py" + workflow_dispatch: + inputs: + target: + description: "Deploy To" + required: true + type: choice + options: + - dev + - test + - sandbox + - prod + +jobs: + pay-queue-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "pay-queue" + working_directory: "./pay-queue" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} diff --git a/.github/workflows/payment-jobs-cd-gcp.yml b/.github/workflows/payment-jobs-cd-gcp.yml new file mode 100644 index 000000000..ea77e4190 --- /dev/null +++ b/.github/workflows/payment-jobs-cd-gcp.yml @@ -0,0 +1,32 @@ +name: Payment Jobs CD GCP + +on: + push: + branches: + - feature-gcp + paths: + - "jobs/payment-jobs/**" + - "pay-api/src/pay_api/models/**" + - "pay-api/src/pay_api/services/cfs_service.py" + workflow_dispatch: + inputs: + target: + description: "Deploy To" + required: true + type: choice + options: + - dev + - test + - sandbox + - prod + +jobs: + payment-jobs-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-job-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "payment-jobs" + working_directory: "./jobs/payment-jobs" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} diff --git a/.github/workflows/payment-jobs-ci.yml b/.github/workflows/payment-jobs-ci.yml index deb7e315c..7a10f3519 100644 --- a/.github/workflows/payment-jobs-ci.yml +++ b/.github/workflows/payment-jobs-ci.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - feature-queue-python-upgrade paths: - "jobs/payment-jobs/**" - "pay-api/src/pay_api/models/**" @@ -21,7 +22,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -30,10 +31,10 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -53,7 +54,7 @@ jobs: testing: needs: setup-job env: - DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/postgres" + DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/pay-test" USE_DOCKER_MOCK: "YES" JWT_OIDC_ISSUER: "http://localhost:8081/auth/realms/demo" SBC_AUTH_ADMIN_CLIENT_ID: "sbc-auth-admin" @@ -61,20 +62,24 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres + POSTGRES_DB: pay-test ports: - 5432:5432 # needed because the postgres container does not provide a healthcheck options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -98,8 +103,12 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/ppr-report-api-cd.yml b/.github/workflows/ppr-report-api-cd.yml index 054547381..b37d3d0a5 100644 --- a/.github/workflows/ppr-report-api-cd.yml +++ b/.github/workflows/ppr-report-api-cd.yml @@ -31,7 +31,7 @@ jobs: name: "dev" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login Openshift shell: bash @@ -77,7 +77,7 @@ jobs: name: "${{ github.event.inputs.environment }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env by input run: | echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV diff --git a/.github/workflows/report-api-cd.yml b/.github/workflows/report-api-cd.yml index 2611a7346..e9a05b7d8 100644 --- a/.github/workflows/report-api-cd.yml +++ b/.github/workflows/report-api-cd.yml @@ -31,7 +31,7 @@ jobs: name: "dev" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login Openshift shell: bash @@ -77,7 +77,7 @@ jobs: name: "${{ github.event.inputs.environment }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env by input run: | echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV diff --git a/.github/workflows/report-api-ci.yml b/.github/workflows/report-api-ci.yml index 391057ff6..96d313e70 100644 --- a/.github/workflows/report-api-ci.yml +++ b/.github/workflows/report-api-ci.yml @@ -19,7 +19,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -28,10 +28,10 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -61,6 +61,10 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 @@ -74,7 +78,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -98,8 +102,12 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.gitignore b/.gitignore index ebb819ae6..ba3b879e1 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ coverage.xml .hypothesis/ .pytest_cache/ pytest.xml +test_eft_tdi17.txt # Translations *.mo @@ -124,8 +125,9 @@ package-lock.json cas_settlement_file.csv jobs/payment-jobs/tests/docker/ftp ACK.INBOX.F12022020202 -queue_services/payment-reconciliations/ACK.* -queue_services/payment-reconciliations/FEEDBACK.* + +pay-queue/ACK.* +pay-queue/FEEDBACK.* jobs/notebook-report/data/ test_eft_tdi17.txt ACK.INBOX* diff --git a/bcol-api/Dockerfile b/bcol-api/Dockerfile index f31a0a12f..7c8145bf7 100644 --- a/bcol-api/Dockerfile +++ b/bcol-api/Dockerfile @@ -1,39 +1,94 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye as development_build ARG VCS_REF="missing" ARG BUILD_DATE="missing" ENV VCS_REF=${VCS_REF} ENV BUILD_DATE=${BUILD_DATE} +ENV PORT=8080 LABEL org.label-schema.vcs-ref=${VCS_REF} \ org.label-schema.build-date=${BUILD_DATE} USER root +LABEL maintainer="travissemple" +LABEL vendor="BCROS" + +ARG APP_ENV \ + # Needed for fixing permissions of files created by Docker: + UID=1000 \ + GID=1000 + +ENV APP_ENV=${APP_ENV} \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_DEFAULT_TIMEOUT=100 \ + PIP_ROOT_USER_ACTION=ignore \ + # poetry: + POETRY_VERSION=1.8.3 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + POETRY_HOME='/usr/local' + +SHELL ["/bin/bash", "-eo", "pipefail", "-c"] + +RUN apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y \ + bash \ + brotli \ + build-essential \ + curl \ + gettext \ + git \ + libpq-dev \ + wait-for-it \ + && curl -sSL 'https://install.python-poetry.org' | python - \ + && poetry --version \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* + RUN apt-get update && apt-get install -y libsasl2-dev python-dev libldap2-dev libssl-dev libsnmp-dev WORKDIR / COPY ["scripts/openssl.cnf","/etc/ssl/"] -# Create working directory -RUN mkdir /opt/app-root && chmod 755 /opt/app-root -WORKDIR /opt/app-root +WORKDIR /code -# Install the requirements -COPY ./requirements.txt . +RUN groupadd -g "${GID}" -r web \ + && useradd -d '/code' -g web -l -r -u "${UID}" web \ + && chown web:web -R '/code' -RUN pip install --upgrade pip -RUN pip install --no-cache-dir -r requirements.txt +# Copy only requirements, to cache them in docker layer +COPY --chown=web:web ./poetry.lock ./pyproject.toml /code/ -COPY . . +COPY --chown=web:web ./src /code/src +COPY --chown=web:web ./README.md /code -RUN pip install . +# Project initialization: +RUN --mount=type=cache,target="$POETRY_CACHE_DIR" \ + echo "$APP_ENV" \ + && poetry version \ + # Install deps: + && poetry run pip install -U pip \ + && poetry install \ + $(if [ -z ${APP_ENV+x} ] | [ "$APP_ENV" = 'production' ]; then echo '--only main'; fi) \ + --no-interaction --no-ansi -USER 1001 +# Running as non-root user: +USER web -# Set Python path -ENV PYTHONPATH=/opt/app-root/src +# The following stage is only for production: +FROM development_build AS production_build +COPY --chown=web:web . /code -ENTRYPOINT ["bash", "docker-entrypoint.sh"] +CMD gunicorn --bind 0.0.0.0:${PORT} --config /code/gunicorn_config.py wsgi:app diff --git a/bcol-api/Makefile b/bcol-api/Makefile index 66d72527e..5678903cc 100755 --- a/bcol-api/Makefile +++ b/bcol-api/Makefile @@ -12,7 +12,7 @@ DOCKER_NAME:=bcol-api ################################################################################# # COMMANDS -- Setup # ################################################################################# -setup: install install-dev ## Setup the project +setup: install ## Setup the project clean: clean-build clean-pyc clean-test ## Clean the project rm -rf venv/ @@ -36,19 +36,9 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements/prod.txt ;\ - pip freeze | sort > requirements.txt ;\ - cat requirements/repo-libraries.txt >> requirements.txt ;\ - pip install -Ur requirements/repo-libraries.txt ;\ - pip install -Ur requirements/dev.txt ;\ - touch venv/bin/activate # update so it's as new as requirements/prod.txt - -install-dev: ## Install local application - . venv/bin/activate && pip install -e . +install: clean + pip install poetry ;\ + poetry install ################################################################################# # COMMANDS - CI # @@ -56,15 +46,15 @@ install-dev: ## Install local application ci: lint flake8 test ## CI flow pylint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg src/$(PROJECT_NAME) + poetry run pylint --rcfile=setup.cfg src/$(PROJECT_NAME) flake8: ## Linting with flake8 - . venv/bin/activate && flake8 src/$(PROJECT_NAME) tests + poetry run flake8 src/$(PROJECT_NAME) tests lint: pylint flake8 ## run all lint type scripts test: ## Unit testing - . venv/bin/activate && pytest + poetry run pytest mac-cov: test ## Run the coverage report and display in a browser window (mac) @open -a "Google Chrome" htmlcov/index.html @@ -124,7 +114,7 @@ tag: push ## tag image # COMMANDS - Local # ################################################################################# run: ## Run the project in local - . venv/bin/activate && python -m flask run -p 5000 + poetry run flask run -p 5000 ################################################################################# # Self Documenting Commands # diff --git a/bcol-api/docker-entrypoint.sh b/bcol-api/docker-entrypoint.sh deleted file mode 100755 index ef5abdf26..000000000 --- a/bcol-api/docker-entrypoint.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -echo 'starting application' -gunicorn -b 0.0.0.0:8080 wsgi:application diff --git a/bcol-api/poetry.lock b/bcol-api/poetry.lock new file mode 100644 index 000000000..7c1b76992 --- /dev/null +++ b/bcol-api/poetry.lock @@ -0,0 +1,1785 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "aniso8601" +version = "9.0.1" +description = "A library for parsing ISO 8601 strings." +optional = false +python-versions = "*" +files = [ + {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"}, + {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"}, +] + +[package.extras] +dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"] + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "autopep8" +version = "2.0.4" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.6" +files = [ + {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, + {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, +] + +[package.dependencies] +pycodestyle = ">=2.10.0" + +[[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 = "cachelib" +version = "0.12.0" +description = "A collection of cache libraries in the same API interface." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cachelib-0.12.0-py3-none-any.whl", hash = "sha256:038f4d855afc3eb8caab10458f6eac55c328911f9055824c22c2f259ef9ed3a3"}, + {file = "cachelib-0.12.0.tar.gz", hash = "sha256:8243029a028436fd23229113dee517c0700bb43a8a289ec5a963e4af9ca2b194"}, +] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.4.3" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "flake8" +version = "7.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "flake8-blind-except" +version = "0.2.1" +description = "A flake8 extension that checks for blind except: statements" +optional = false +python-versions = "*" +files = [ + {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, +] + +[[package]] +name = "flake8-debugger" +version = "4.1.2" +description = "ipdb/pdb statement checker plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-isort" +version = "6.1.1" +description = "flake8 plugin that integrates isort" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_isort-6.1.1-py3-none-any.whl", hash = "sha256:0fec4dc3a15aefbdbe4012e51d5531a2eb5fa8b981cdfbc882296a59b54ede12"}, + {file = "flake8_isort-6.1.1.tar.gz", hash = "sha256:c1f82f3cf06a80c13e1d09bfae460e9666255d5c780b859f19f8318d420370b3"}, +] + +[package.dependencies] +flake8 = "*" +isort = ">=5.0.0,<6" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "flake8-quotes" +version = "3.4.0" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, +] + +[package.dependencies] +flake8 = "*" +setuptools = "*" + +[[package]] +name = "flask" +version = "3.0.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, +] + +[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 = "flask_jwt_oidc" +version = "0.3.0" +description = "Flask JWT OIDC" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +cachelib = "*" +flask = "*" +python-jose = "*" +six = "*" + +[package.source] +type = "git" +url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +reference = "HEAD" +resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" + +[[package]] +name = "flask-moment" +version = "1.0.5" +description = "Formatting of dates and times in Flask templates using moment.js." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Moment-1.0.5.tar.gz", hash = "sha256:33307ecd4af8290b6df6a9828ff55ac0977d0567817f9bc0f69803d22ed2b55c"}, + {file = "Flask_Moment-1.0.5-py3-none-any.whl", hash = "sha256:6e7b3eef89e2137bbbee975405f241a68a44edfa34bf052c92d84364992adca6"}, +] + +[package.dependencies] +Flask = "*" +packaging = ">=14.1" + +[[package]] +name = "flask-opentracing" +version = "1.1.0" +description = "OpenTracing support for Flask applications" +optional = false +python-versions = "*" +files = [ + {file = "Flask-OpenTracing-1.1.0.tar.gz", hash = "sha256:a9a39d367fbe7e9ed9c77b90ac48159c1a3e82982a5abf84d3f4d710d24580ac"}, +] + +[package.dependencies] +Flask = "*" +opentracing = ">=2.0,<3" + +[package.extras] +tests = ["flake8", "flake8-quotes", "mock", "pytest", "pytest-cov"] + +[[package]] +name = "flask-restx" +version = "1.3.0" +description = "Fully featured framework for fast, easy and documented API development with Flask" +optional = false +python-versions = "*" +files = [ + {file = "flask-restx-1.3.0.tar.gz", hash = "sha256:4f3d3fa7b6191fcc715b18c201a12cd875176f92ba4acc61626ccfd571ee1728"}, + {file = "flask_restx-1.3.0-py2.py3-none-any.whl", hash = "sha256:636c56c3fb3f2c1df979e748019f084a938c4da2035a3e535a4673e4fc177691"}, +] + +[package.dependencies] +aniso8601 = ">=0.82" +Flask = ">=0.8,<2.0.0 || >2.0.0" +importlib-resources = "*" +jsonschema = "*" +pytz = "*" +werkzeug = "!=2.0.0" + +[package.extras] +dev = ["Faker (==2.0.0)", "black", "blinker", "invoke (==2.2.0)", "mock (==3.0.5)", "pytest (==7.0.1)", "pytest-benchmark (==3.4.1)", "pytest-cov (==4.0.0)", "pytest-flask (==1.3.0)", "pytest-mock (==3.6.1)", "pytest-profiling (==1.7.0)", "setuptools", "tox", "twine (==3.8.0)", "tzlocal"] +doc = ["Sphinx (==5.3.0)", "alabaster (==0.7.12)", "sphinx-issues (==3.0.1)"] +test = ["Faker (==2.0.0)", "blinker", "invoke (==2.2.0)", "mock (==3.0.5)", "pytest (==7.0.1)", "pytest-benchmark (==3.4.1)", "pytest-cov (==4.0.0)", "pytest-flask (==1.3.0)", "pytest-mock (==3.6.1)", "pytest-profiling (==1.7.0)", "setuptools", "twine (==3.8.0)", "tzlocal"] + +[[package]] +name = "flask-script" +version = "2.0.6" +description = "Scripting support for Flask" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Script-2.0.6.tar.gz", hash = "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65"}, +] + +[package.dependencies] +Flask = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +description = "Add SQLAlchemy support to your Flask application." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[package.dependencies] +flask = ">=2.2.5" +sqlalchemy = ">=2.0.16" + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "importlib-resources" +version = "6.3.0" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_resources-6.3.0-py3-none-any.whl", hash = "sha256:783407aa1cd05550e3aa123e8f7cfaebee35ffa9cb0242919e2d1e4172222705"}, + {file = "importlib_resources-6.3.0.tar.gz", hash = "sha256:166072a97e86917a9025876f34286f549b9caf1d10b35a1b372bffa1600c6569"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["jaraco.collections", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isodate" +version = "0.6.1" +description = "An ISO 8601 date/time/duration parser and formatter" +optional = false +python-versions = "*" +files = [ + {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"}, + {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"}, +] + +[package.dependencies] +six = "*" + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[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 = "jaeger-client" +version = "4.8.0" +description = "Jaeger Python OpenTracing Tracer implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaeger-client-4.8.0.tar.gz", hash = "sha256:3157836edab8e2c209bd2d6ae61113db36f7ee399e66b1dcbb715d87ab49bfe0"}, +] + +[package.dependencies] +opentracing = ">=2.1,<3.0" +threadloop = ">=1,<2" +thrift = "*" +tornado = ">=4.3" + +[package.extras] +tests = ["codecov", "coverage", "flake8", "flake8-quotes", "flake8-typing-imports", "mock", "mypy", "opentracing_instrumentation (>=3,<4)", "prometheus_client (==0.11.0)", "pycurl", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "pytest-localserver", "pytest-timeout", "pytest-tornado", "tchannel (==2.1.0)"] + +[[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 = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "lovely-pytest-docker" +version = "0.3.1" +description = "Pytest testing utilities with docker containers." +optional = false +python-versions = "*" +files = [ + {file = "lovely-pytest-docker-0.3.1.tar.gz", hash = "sha256:4326a180bfd4dd4ad69c2ef3e3643c41075d965f40068488b40204602e6df85e"}, +] + +[package.dependencies] +pytest = "*" +six = "*" + +[[package]] +name = "lxml" +version = "5.1.0" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +optional = false +python-versions = ">=3.6" +files = [ + {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, + {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, + {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, + {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, + {file = "lxml-5.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2befa20a13f1a75c751f47e00929fb3433d67eb9923c2c0b364de449121f447c"}, + {file = "lxml-5.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22b7ee4c35f374e2c20337a95502057964d7e35b996b1c667b5c65c567d2252a"}, + {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bf8443781533b8d37b295016a4b53c1494fa9a03573c09ca5104550c138d5c05"}, + {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, + {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, + {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, + {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, + {file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af8920ce4a55ff41167ddbc20077f5698c2e710ad3353d32a07d3264f3a2021e"}, + {file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cfced4a069003d8913408e10ca8ed092c49a7f6cefee9bb74b6b3e860683b45"}, + {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9e5ac3437746189a9b4121db2a7b86056ac8786b12e88838696899328fc44bb2"}, + {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, + {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, + {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, + {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, + {file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16dd953fb719f0ffc5bc067428fc9e88f599e15723a85618c45847c96f11f431"}, + {file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16018f7099245157564d7148165132c70adb272fb5a17c048ba70d9cc542a1a1"}, + {file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82cd34f1081ae4ea2ede3d52f71b7be313756e99b4b5f829f89b12da552d3aa3"}, + {file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:19a1bc898ae9f06bccb7c3e1dfd73897ecbbd2c96afe9095a6026016e5ca97b8"}, + {file = "lxml-5.1.0-cp312-cp312-win32.whl", hash = "sha256:13521a321a25c641b9ea127ef478b580b5ec82aa2e9fc076c86169d161798b01"}, + {file = "lxml-5.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:1ad17c20e3666c035db502c78b86e58ff6b5991906e55bdbef94977700c72623"}, + {file = "lxml-5.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:24ef5a4631c0b6cceaf2dbca21687e29725b7c4e171f33a8f8ce23c12558ded1"}, + {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d2900b7f5318bc7ad8631d3d40190b95ef2aa8cc59473b73b294e4a55e9f30f"}, + {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:601f4a75797d7a770daed8b42b97cd1bb1ba18bd51a9382077a6a247a12aa38d"}, + {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4b68c961b5cc402cbd99cca5eb2547e46ce77260eb705f4d117fd9c3f932b95"}, + {file = "lxml-5.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:afd825e30f8d1f521713a5669b63657bcfe5980a916c95855060048b88e1adb7"}, + {file = "lxml-5.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:262bc5f512a66b527d026518507e78c2f9c2bd9eb5c8aeeb9f0eb43fcb69dc67"}, + {file = "lxml-5.1.0-cp36-cp36m-win32.whl", hash = "sha256:e856c1c7255c739434489ec9c8aa9cdf5179785d10ff20add308b5d673bed5cd"}, + {file = "lxml-5.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:c7257171bb8d4432fe9d6fdde4d55fdbe663a63636a17f7f9aaba9bcb3153ad7"}, + {file = "lxml-5.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9e240ae0ba96477682aa87899d94ddec1cc7926f9df29b1dd57b39e797d5ab5"}, + {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a96f02ba1bcd330807fc060ed91d1f7a20853da6dd449e5da4b09bfcc08fdcf5"}, + {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3898ae2b58eeafedfe99e542a17859017d72d7f6a63de0f04f99c2cb125936"}, + {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61c5a7edbd7c695e54fca029ceb351fc45cd8860119a0f83e48be44e1c464862"}, + {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3aeca824b38ca78d9ee2ab82bd9883083d0492d9d17df065ba3b94e88e4d7ee6"}, + {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, + {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, + {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, + {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, + {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, + {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, + {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:98f3f020a2b736566c707c8e034945c02aa94e124c24f77ca097c446f81b01f1"}, + {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, + {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, + {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, + {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, + {file = "lxml-5.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f8b0c78e7aac24979ef09b7f50da871c2de2def043d468c4b41f512d831e912"}, + {file = "lxml-5.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bcf86dfc8ff3e992fed847c077bd875d9e0ba2fa25d859c3a0f0f76f07f0c8d"}, + {file = "lxml-5.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:49a9b4af45e8b925e1cd6f3b15bbba2c81e7dba6dce170c677c9cda547411e14"}, + {file = "lxml-5.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:280f3edf15c2a967d923bcfb1f8f15337ad36f93525828b40a0f9d6c2ad24890"}, + {file = "lxml-5.1.0-cp39-cp39-win32.whl", hash = "sha256:ed7326563024b6e91fef6b6c7a1a2ff0a71b97793ac33dbbcf38f6005e51ff6e"}, + {file = "lxml-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:8d7b4beebb178e9183138f552238f7e6613162a42164233e2bda00cb3afac58f"}, + {file = "lxml-5.1.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9bd0ae7cc2b85320abd5e0abad5ccee5564ed5f0cc90245d2f9a8ef330a8deae"}, + {file = "lxml-5.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c1d679df4361408b628f42b26a5d62bd3e9ba7f0c0e7969f925021554755aa"}, + {file = "lxml-5.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2ad3a8ce9e8a767131061a22cd28fdffa3cd2dc193f399ff7b81777f3520e372"}, + {file = "lxml-5.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:304128394c9c22b6569eba2a6d98392b56fbdfbad58f83ea702530be80d0f9df"}, + {file = "lxml-5.1.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d74fcaf87132ffc0447b3c685a9f862ffb5b43e70ea6beec2fb8057d5d2a1fea"}, + {file = "lxml-5.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:8cf5877f7ed384dabfdcc37922c3191bf27e55b498fecece9fd5c2c7aaa34c33"}, + {file = "lxml-5.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:877efb968c3d7eb2dad540b6cabf2f1d3c0fbf4b2d309a3c141f79c7e0061324"}, + {file = "lxml-5.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f14a4fb1c1c402a22e6a341a24c1341b4a3def81b41cd354386dcb795f83897"}, + {file = "lxml-5.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:25663d6e99659544ee8fe1b89b1a8c0aaa5e34b103fab124b17fa958c4a324a6"}, + {file = "lxml-5.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8b9f19df998761babaa7f09e6bc169294eefafd6149aaa272081cbddc7ba4ca3"}, + {file = "lxml-5.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e53d7e6a98b64fe54775d23a7c669763451340c3d44ad5e3a3b48a1efbdc96f"}, + {file = "lxml-5.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c3cd1fc1dc7c376c54440aeaaa0dcc803d2126732ff5c6b68ccd619f2e64be4f"}, + {file = "lxml-5.1.0.tar.gz", hash = "sha256:3eea6ed6e6c918e468e693c41ef07f3c3acc310b70ddd9cc72d9ef84bc9564ca"}, +] + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=3.0.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "opentracing" +version = "2.4.0" +description = "OpenTracing API for Python. See documentation at http://opentracing.io" +optional = false +python-versions = "*" +files = [ + {file = "opentracing-2.4.0.tar.gz", hash = "sha256:a173117e6ef580d55874734d1fa7ecb6f3655160b8b8974a2a1e98e5ec9c840d"}, +] + +[package.extras] +tests = ["Sphinx", "doubles", "flake8", "flake8-quotes", "gevent", "mock", "pytest", "pytest-cov", "pytest-mock", "six (>=1.10.0,<2.0)", "sphinx_rtd_theme", "tornado"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[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 = "pyasn1" +version = "0.5.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pycountry" +version = "23.12.11" +description = "ISO country, subdivision, language, currency and script definitions and their translations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycountry-23.12.11-py3-none-any.whl", hash = "sha256:2ff91cff4f40ff61086e773d61e72005fe95de4a57bfc765509db05695dc50ab"}, + {file = "pycountry-23.12.11.tar.gz", hash = "sha256:00569d82eaefbc6a490a311bfa84a9c571cff9ddbf8b0a4f4e7b4f868b4ad925"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pyhamcrest" +version = "2.1.0" +description = "Hamcrest framework for matcher objects" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyhamcrest-2.1.0-py3-none-any.whl", hash = "sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587"}, + {file = "pyhamcrest-2.1.0.tar.gz", hash = "sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c"}, +] + +[package.extras] +dev = ["black", "doc2dash", "flake8", "pyhamcrest[docs,tests]", "pytest-mypy", "towncrier", "tox", "tox-asdf", "twine"] +docs = ["alabaster (>=0.7,<1.0)", "sphinx (>=4.0,<5.0)"] +tests = ["coverage[toml]", "dataclasses", "mypy (!=0.940)", "pytest (>=5.0)", "pytest-mypy-plugins", "pytest-sugar", "pytest-xdist", "pyyaml", "types-dataclasses", "types-mock"] +tests-numpy = ["numpy", "pyhamcrest[tests]"] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint-flask" +version = "0.6" +description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" +optional = false +python-versions = "*" +files = [ + {file = "pylint-flask-0.6.tar.gz", hash = "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517"}, +] + +[package.dependencies] +pylint-plugin-utils = ">=0.2.1" + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + +[[package]] +name = "pyrsistent" +version = "0.20.0" +description = "Persistent/Functional/Immutable data structures" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, + {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, + {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, + {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, + {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, + {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, + {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, +] + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.12.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, +] + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "python-ldap" +version = "3.4.4" +description = "Python modules for implementing LDAP clients" +optional = false +python-versions = ">=3.6" +files = [ + {file = "python-ldap-3.4.4.tar.gz", hash = "sha256:7edb0accec4e037797705f3a05cbf36a9fde50d08c8f67f2aef99a2628fab828"}, +] + +[package.dependencies] +pyasn1 = ">=0.3.7" +pyasn1_modules = ">=0.1.5" + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-file" +version = "2.0.0" +description = "File transport adapter for Requests" +optional = false +python-versions = "*" +files = [ + {file = "requests-file-2.0.0.tar.gz", hash = "sha256:20c5931629c558fda566cacc10cfe2cd502433e628f568c34c80d96a0cc95972"}, + {file = "requests_file-2.0.0-py2.py3-none-any.whl", hash = "sha256:3e493d390adb44aa102ebea827a48717336d5268968c370eaf19abaf5cae13bf"}, +] + +[package.dependencies] +requests = ">=1.0.0" + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +description = "A utility belt for advanced users of python-requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, + {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "sbc_common_components" +version = "0.0.0" +description = "" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +flask = "*" +flask-jwt-oidc = ">=0.1.5" +Flask-OpenTracing = "1.1.0" +Flask-SQLAlchemy = "*" +jaeger-client = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-common-components.git" +reference = "HEAD" +resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +subdirectory = "python" + +[[package]] +name = "sentry-sdk" +version = "1.42.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"}, + {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"}, +] + +[package.dependencies] +blinker = {version = ">=1.1", optional = true, markers = "extra == \"flask\""} +certifi = "*" +flask = {version = ">=0.11", optional = true, markers = "extra == \"flask\""} +markupsafe = {version = "*", optional = true, markers = "extra == \"flask\""} +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "69.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[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 = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.28" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "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\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "threadloop" +version = "1.0.2" +description = "Tornado IOLoop Backed Concurrent Futures" +optional = false +python-versions = "*" +files = [ + {file = "threadloop-1.0.2-py2-none-any.whl", hash = "sha256:5c90dbefab6ffbdba26afb4829d2a9df8275d13ac7dc58dccb0e279992679599"}, + {file = "threadloop-1.0.2.tar.gz", hash = "sha256:8b180aac31013de13c2ad5c834819771992d350267bddb854613ae77ef571944"}, +] + +[package.dependencies] +tornado = "*" + +[[package]] +name = "thrift" +version = "0.16.0" +description = "Python bindings for the Apache Thrift RPC system" +optional = false +python-versions = "*" +files = [ + {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, +] + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[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)"] + +[[package]] +name = "zeep" +version = "4.2.1" +description = "A Python SOAP client" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zeep-4.2.1-py3-none-any.whl", hash = "sha256:6754feb4c34a4b6d65fbc359252bf6654dcce3937bf1d95aae4402a60a8f5939"}, + {file = "zeep-4.2.1.tar.gz", hash = "sha256:72093acfdb1d8360ed400869b73fbf1882b95c4287f798084c42ee0c1ff0e425"}, +] + +[package.dependencies] +attrs = ">=17.2.0" +isodate = ">=0.5.4" +lxml = ">=4.6.0" +platformdirs = ">=1.4.0" +pytz = "*" +requests = ">=2.7.0" +requests-file = ">=1.5.1" +requests-toolbelt = ">=0.7.1" + +[package.extras] +async = ["httpx (>=0.15.0)"] +docs = ["sphinx (>=1.4.0)"] +test = ["coverage[toml] (==5.2.1)", "flake8 (==3.8.3)", "flake8-blind-except (==0.1.1)", "flake8-debugger (==3.2.1)", "flake8-imports (==0.1.1)", "freezegun (==0.3.15)", "isort (==5.3.2)", "pretend (==1.0.9)", "pytest (==6.2.5)", "pytest-asyncio", "pytest-cov (==2.8.1)", "pytest-httpx", "requests-mock (>=0.7.0)"] +xmlsec = ["xmlsec (>=0.6.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "bf95b2ec5b5f61f7f67c38f71ebdee994d018d51ebe73d16759363cc358c6f0c" diff --git a/bcol-api/pyproject.toml b/bcol-api/pyproject.toml new file mode 100644 index 000000000..ec6d9a19e --- /dev/null +++ b/bcol-api/pyproject.toml @@ -0,0 +1,55 @@ +[tool.poetry] +name = "bcol-api" +version = "0.1.0" +description = "" +authors = ["BC Registries"] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +gunicorn = "^21.2.0" +flask = "^3.0.2" +flask-script = "^2.0.6" +flask-moment = "^1.0.5" +flask-restx = "^1.3.0" +python-dotenv = "^1.0.1" +psycopg2-binary = "^2.9.9" +jsonschema = "4.17.3" +requests = "^2.31.0" +zeep = "^4.2.1" +python-ldap = "^3.4.4" +sentry-sdk = {extras = ["flask"], version = "^1.42.0"} +attrs = "^23.2.0" +werkzeug = "^3.0.1" +jaeger-client = "^4.8.0" +pycountry = "^23.12.11" +itsdangerous = "^2.1.2" +jinja2 = "^3.1.3" + + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pytest-mock = "^3.12.0" +requests = "^2.31.0" +pyhamcrest = "^2.1.0" +pytest-cov = "^4.1.0" +flake8 = "^7.0.0" +flake8-blind-except = "^0.2.1" +flake8-debugger = "^4.1.2" +flake8-docstrings = "^1.7.0" +flake8-isort = "^6.1.1" +flake8-quotes = "^3.4.0" +pep8-naming = "^0.13.3" +autopep8 = "^2.0.4" +coverage = "^7.4.3" +pylint = "^3.1.0" +pylint-flask = "^0.6" +pydocstyle = "^6.3.0" +lovely-pytest-docker = "^0.3.1" +isort = "^5.13.2" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/bcol-api/requirements.txt b/bcol-api/requirements.txt deleted file mode 100644 index 0bc296f42..000000000 --- a/bcol-api/requirements.txt +++ /dev/null @@ -1,51 +0,0 @@ -Flask-Moment==1.0.5 -Flask-Script==2.0.6 -Flask==1.1.2 -Jinja2==3.0.3 -MarkupSafe==2.1.1 -Werkzeug==1.0.1 -aniso8601==9.0.1 -attrs==22.1.0 -blinker==1.5 -cachelib==0.9.0 -certifi==2023.7.22 -charset-normalizer==2.1.1 -click==8.1.3 -ecdsa==0.18.0 -flask-jwt-oidc==0.3.0 -flask-restx==1.0.3 -gunicorn==20.1.0 -idna==3.7 -importlib-resources==5.10.1 -isodate==0.6.1 -itsdangerous==2.0.1 -jaeger-client==4.8.0 -jsonschema==4.17.3 -lxml==4.9.1 -opentracing==2.4.0 -packaging==21.3 -pkgutil_resolve_name==1.3.10 -platformdirs==2.5.4 -psycopg2-binary==2.9.5 -pyasn1-modules==0.2.8 -pyasn1==0.4.8 -pycountry==22.3.5 -pyparsing==3.0.9 -pyrsistent==0.19.2 -python-dotenv==0.21.0 -python-jose==3.3.0 -python-ldap==3.4.3 -pytz==2022.6 -requests-file==1.5.1 -requests-toolbelt==0.10.1 -requests==2.32.2 -rsa==4.9 -sentry-sdk==1.14.0 -six==1.16.0 -threadloop==1.0.2 -thrift==0.16.0 -tornado==6.3.3 -urllib3==1.26.17 -zeep==4.2.1 -zipp==3.11.0 --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python diff --git a/bcol-api/requirements/dev.txt b/bcol-api/requirements/dev.txt deleted file mode 100755 index 098dd2013..000000000 --- a/bcol-api/requirements/dev.txt +++ /dev/null @@ -1,25 +0,0 @@ -# Everything the developer needs in addition to the production requirements --r prod.txt - -# Testing -pytest -pytest-mock -requests -pyhamcrest -pytest-cov - -# Lint and code style -flake8==5.0.4 -flake8-blind-except -flake8-debugger -flake8-docstrings -flake8-isort -flake8-quotes -pep8-naming -autopep8 -coverage -pylint -pylint-flask -pydocstyle -lovely-pytest-docker -isort diff --git a/bcol-api/requirements/prod.txt b/bcol-api/requirements/prod.txt deleted file mode 100755 index 1f92220e0..000000000 --- a/bcol-api/requirements/prod.txt +++ /dev/null @@ -1,19 +0,0 @@ -gunicorn -Flask -Flask-Script -Flask-Moment -flask-restx -flask-jwt-oidc -python-dotenv -psycopg2-binary -jsonschema==4.17.3 -requests -zeep -python-ldap -sentry-sdk[flask] -attrs -Werkzeug<2 -jaeger-client -pycountry -itsdangerous==2.0.1 -Jinja2==3.0.3 diff --git a/bcol-api/requirements/repo-libraries.txt b/bcol-api/requirements/repo-libraries.txt deleted file mode 100644 index 3b0252bf2..000000000 --- a/bcol-api/requirements/repo-libraries.txt +++ /dev/null @@ -1 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python diff --git a/bcol-api/setup.cfg b/bcol-api/setup.cfg index 1fc623352..7b0dffc81 100755 --- a/bcol-api/setup.cfg +++ b/bcol-api/setup.cfg @@ -17,7 +17,7 @@ keywords = [options] zip_safe = True -python_requires = >=3.6 +python_requires = >=3.12 include_package_data = True packages = find: diff --git a/bcol-api/src/bcol_api/resources/__init__.py b/bcol-api/src/bcol_api/resources/__init__.py index d094b94c9..28f382357 100755 --- a/bcol-api/src/bcol_api/resources/__init__.py +++ b/bcol-api/src/bcol_api/resources/__init__.py @@ -33,7 +33,6 @@ __all__ = ('API_BLUEPRINT', 'OPS_BLUEPRINT') # This will add the Authorize button to the swagger docs -# TODO oauth2 & openid may not yet be supported by restplus <- check on this AUTHORIZATIONS = {'apikey': {'type': 'apiKey', 'in': 'header', 'name': 'Authorization'}} OPS_BLUEPRINT = Blueprint('API_OPS', __name__, url_prefix='/ops') diff --git a/bcol-api/src/bcol_api/resources/bcol_payment.py b/bcol-api/src/bcol_api/resources/bcol_payment.py index dd8536bf7..ddd2d3669 100755 --- a/bcol-api/src/bcol_api/resources/bcol_payment.py +++ b/bcol-api/src/bcol_api/resources/bcol_payment.py @@ -24,7 +24,6 @@ from bcol_api.utils.auth import jwt as _jwt from bcol_api.utils.constants import Role from bcol_api.utils.errors import Error -from bcol_api.utils.trace import tracing as _tracing from bcol_api.utils.util import cors_preflight @@ -37,7 +36,6 @@ class AccountPayment(Resource): """Endpoint resource to manage BCOL Payments.""" @staticmethod - @_tracing.trace() @_jwt.requires_auth @cors.crossdomain(origin='*') def post(): diff --git a/bcol-api/src/bcol_api/resources/bcol_profile.py b/bcol-api/src/bcol_api/resources/bcol_profile.py index 36a61a7ff..5b1c565d9 100755 --- a/bcol-api/src/bcol_api/resources/bcol_profile.py +++ b/bcol-api/src/bcol_api/resources/bcol_profile.py @@ -23,7 +23,6 @@ from bcol_api.services.bcol_profile import BcolProfile as BcolProfileService from bcol_api.utils.auth import jwt as _jwt from bcol_api.utils.errors import Error -from bcol_api.utils.trace import tracing as _tracing from bcol_api.utils.util import cors_preflight @@ -36,7 +35,6 @@ class BcolProfiles(Resource): """Endpoint query bcol profile using user id and password.""" @staticmethod - @_tracing.trace() @_jwt.requires_auth @cors.crossdomain(origin='*') def post(): @@ -61,7 +59,6 @@ class BcolProfile(Resource): """Endpoint resource to get bcol profile by user id.""" @staticmethod - @_tracing.trace() @_jwt.has_one_of_roles(['system']) @cors.crossdomain(origin='*') def get(bcol_user_id: str): diff --git a/bcol-api/src/bcol_api/utils/logging.py b/bcol-api/src/bcol_api/utils/logging.py index 8b88ddcf2..8568f87dd 100755 --- a/bcol-api/src/bcol_api/utils/logging.py +++ b/bcol-api/src/bcol_api/utils/logging.py @@ -18,12 +18,7 @@ def setup_logging(conf): - """Create the services logger. - - TODO should be reworked to load in the proper loggers and remove others - """ - # log_file_path = path.join(path.abspath(path.dirname(__file__)), conf) - + """Create the services logger.""" if conf and path.isfile(conf): logging.config.fileConfig(conf) print(f'Configure logging, from conf:{conf}', file=sys.stdout) diff --git a/bcol-api/src/bcol_api/utils/trace.py b/bcol-api/src/bcol_api/utils/trace.py index 31cc43080..b62a1646d 100644 --- a/bcol-api/src/bcol_api/utils/trace.py +++ b/bcol-api/src/bcol_api/utils/trace.py @@ -12,11 +12,3 @@ # See the License for the specific language governing permissions and # limitations under the License. """Bring in the Tracer.""" -from sbc_common_components.tracing.api_tracer import ApiTracer -from sbc_common_components.tracing.api_tracing import ApiTracing - - -# initialize tracer -API_TRACER = ApiTracer('BCOL API Services') -tracing = ApiTracing( # pylint: disable=invalid-name; lower case name as used by convention in most Flask apps - API_TRACER.tracer) diff --git a/bcol-api/tests/docker/docker-compose.yml b/bcol-api/tests/docker/docker-compose.yml index 053667323..770a2904c 100644 --- a/bcol-api/tests/docker/docker-compose.yml +++ b/bcol-api/tests/docker/docker-compose.yml @@ -2,7 +2,7 @@ version: "3" services: keycloak: - image: quay.io/keycloak/keycloak:12.0.2 + image: quay.io/keycloak/keycloak:15.0.0 ports: - "8081:8081" environment: diff --git a/bcol-api/tests/unit/services/test_bcol_payment.py b/bcol-api/tests/unit/services/test_bcol_payment.py index ed3648807..07ec69e06 100644 --- a/bcol-api/tests/unit/services/test_bcol_payment.py +++ b/bcol-api/tests/unit/services/test_bcol_payment.py @@ -36,10 +36,5 @@ def test_payment(app, payment_mock): has_non_match_error = True assert has_non_match_error # Success case. - with test_case.assertLogs(level='ERROR') as log: + with test_case.assertNoLogs(level='ERROR'): BcolPayment().create_payment({'serviceFees': 1.50}, False) - has_non_match_error = False - for message in log.output: - if "from BCOL doesn\'t match" in message: - has_non_match_error = True - assert has_non_match_error is False diff --git a/codecov.yaml b/codecov.yaml index 174116e19..e7b9696ed 100644 --- a/codecov.yaml +++ b/codecov.yaml @@ -17,8 +17,8 @@ coverage: - payapi - bcolapi - reportapi - - eventlistenerqueue - paymentjobs + - payqueue ignore: - "^/tests/**/*" # ignore test harness code @@ -49,11 +49,12 @@ flags: paths: - report-api/src/api carryforward: true - eventlistenerqueue: - paths: - - queue_services/events-listener/src/events_listener - carryforward: true paymentjobs: paths: - jobs/payment-jobs/tasks carryforward: true + payqueue: + paths: + - pay-queue/src/pay_queue + carryforward: true + diff --git a/jobs/ftp-poller/Dockerfile b/jobs/ftp-poller/Dockerfile index de6bb744f..616ea93d0 100644 --- a/jobs/ftp-poller/Dockerfile +++ b/jobs/ftp-poller/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye # ======================================================================================================== # Install go-crond (from https://github.com/BCDevOps/go-crond) @@ -36,14 +36,61 @@ RUN mkdir $HOME && chmod 755 $HOME WORKDIR $HOME -# Install the requirements -COPY ./requirements.txt . -COPY ./requirements/ ./requirements/ -COPY . . -RUN pip install --upgrade pip -RUN pip install --no-cache-dir -r requirements.txt +ARG APP_ENV \ + # Needed for fixing permissions of files created by Docker: + UID=1000 \ + GID=1000 + +ENV APP_ENV=${APP_ENV} \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_DEFAULT_TIMEOUT=100 \ + PIP_ROOT_USER_ACTION=ignore \ + # poetry: + POETRY_VERSION=1.8.3 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + POETRY_HOME='/usr/local' + +SHELL ["/bin/bash", "-eo", "pipefail", "-c"] + +RUN apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y \ + bash \ + brotli \ + build-essential \ + curl \ + gettext \ + git \ + libpq-dev \ + wait-for-it \ + && curl -sSL 'https://install.python-poetry.org' | python - \ + && poetry --version \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* +# Copy only requirements, to cache them in docker layer +COPY --chown=web:web ./poetry.lock ./pyproject.toml . + +# Project initialization: +RUN --mount=type=cache,target="$POETRY_CACHE_DIR" \ + echo "$APP_ENV" \ + && poetry version \ + && poetry run pip install -U pip \ + && poetry install \ + $(if [ -z ${APP_ENV+x} ] | [ "$APP_ENV" = 'production' ]; then echo '--only main'; fi) \ + --no-interaction --no-ansi + +COPY . . # Set ownership and permissions diff --git a/jobs/ftp-poller/Makefile b/jobs/ftp-poller/Makefile index 42610f6ae..715e83f48 100644 --- a/jobs/ftp-poller/Makefile +++ b/jobs/ftp-poller/Makefile @@ -12,7 +12,7 @@ DOCKER_NAME:=ftp-poller ################################################################################# # COMMANDS -- Setup # ################################################################################# -setup: install install-dev ## Setup the project +setup: install ## Setup the project clean: clean-build clean-pyc clean-test ## Clean the project rm -rf venv/ @@ -36,25 +36,9 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -build-req: clean ## Upgrade requirements - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements/prod.txt ;\ - pip freeze | sort > requirements.txt ;\ - cat requirements/repo-libraries.txt >> requirements.txt ;\ - pip install -Ur requirements/repo-libraries.txt - -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements.txt - -install-dev: ## Install local application - . venv/bin/activate ; \ - pip install -Ur requirements/dev.txt; \ - pip install -e . +install: clean + pip install poetry ;\ + poetry install ################################################################################# # COMMANDS - CI # @@ -62,15 +46,15 @@ install-dev: ## Install local application ci: lint flake8 test ## CI flow pylint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg tasks tests utils + poetry run pylint --rcfile=setup.cfg tasks tests utils flake8: ## Linting with flake8 - . venv/bin/activate && flake8 tasks tests utils + poetry run flake8 tasks tests utils lint: pylint flake8 ## run all lint type scripts test: ## Unit testing - . venv/bin/activate && pytest + poetry run pytest mac-cov: test ## Run the coverage report and display in a browser window (mac) @open -a "Google Chrome" htmlcov/index.html @@ -131,7 +115,7 @@ tag: push ## tag image ################################################################################# run: ## Run the project in local - . venv/bin/activate && python app.py + echo "unimplememted use docker" ################################################################################# # Self Documenting Commands # diff --git a/jobs/ftp-poller/README.md b/jobs/ftp-poller/README.md index 17f53abe7..e73b38555 100644 --- a/jobs/ftp-poller/README.md +++ b/jobs/ftp-poller/README.md @@ -1,7 +1,7 @@ # SFTP Poller project -Polls the SFTP folder and if a settlement file is found , uploads to minio and sends a Pub/Sub message using gcloud CLI. +Polls the SFTP folder and if a settlement file is found , uploads to minio and sends a pubsub message. ## Openshift commands diff --git a/jobs/ftp-poller/invoke_jobs.py b/jobs/ftp-poller/invoke_jobs.py index eddfc319f..875ff01e8 100755 --- a/jobs/ftp-poller/invoke_jobs.py +++ b/jobs/ftp-poller/invoke_jobs.py @@ -44,9 +44,8 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): dsn=app.config.get('SENTRY_DSN'), integrations=[FlaskIntegration()] ) - app.logger.info(f'<<<< Starting Ftp Poller Job >>>>') + app.logger.info('<<<< Starting Ftp Poller Job >>>>') queue.init_app(app) - db.init_app(app) ma.init_app(app) register_shellcontext(app) diff --git a/jobs/ftp-poller/openshift/ftp-poller-build.json b/jobs/ftp-poller/openshift/ftp-poller-build.json deleted file mode 100644 index 8713416c5..000000000 --- a/jobs/ftp-poller/openshift/ftp-poller-build.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Build template for a FTP Poller job.", - "tags": "flask", - "iconClass": "icon-python" - }, - "name": "${NAME}-build-template" - }, - "objects": [ - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}" - } - }, - { - "kind": "BuildConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}", - "labels": { - "app": "${NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-build" - } - }, - "spec": { - "source": { - "type": "Git", - "git": { - "uri": "${GIT_REPO_URL}", - "ref": "${GIT_REF}" - }, - "contextDir": "${SOURCE_CONTEXT_DIR}" - }, - "strategy": { - "type": "Docker", - "dockerStrategy": { - "dockerfilePath": "${DOCKER_FILE_PATH}" - } - }, - "output": { - "to": { - "kind": "ImageStreamTag", - "name": "${NAME}:${OUTPUT_IMAGE_TAG}" - } - }, - "triggers": [ - { - "type": "ConfigChange" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the objects defined in this template. You should keep this as default unless your know what your doing.", - "required": true, - "value": "ftp-poller" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "GIT_REPO_URL", - "displayName": "Git Repo URL", - "description": "The URL to your GIT repo, don't use the this default unless your just experimenting.", - "required": true, - "value": "https://github.com/bcgov/sbc-pay.git" - }, - { - "name": "GIT_REF", - "displayName": "Git Reference", - "description": "The git reference or branch.", - "required": true, - "value": "development" - }, - { - "name": "SOURCE_CONTEXT_DIR", - "displayName": "Source Context Directory", - "description": "The source context directory.", - "required": true, - "value": "jobs/ftp-poller" - }, - { - "name": "SOURCE_IMAGE_KIND", - "displayName": "Source Image Kind", - "required": true, - "description": "The 'kind' (type) of the source image; typically ImageStreamTag, or DockerImage.", - "value": "ImageStreamTag" - }, - { - "name": "SOURCE_IMAGE_NAME_SPACE", - "displayName": "Source Image Name Space", - "required": true, - "description": "The name space of the source image.", - "value": "d7eovc-tools" - }, - { - "name": "SOURCE_IMAGE_NAME", - "displayName": "Source Image Name", - "required": true, - "description": "The name of the source image.", - "value": "python" - }, - { - "name": "SOURCE_IMAGE_TAG", - "displayName": "Source Image Tag", - "required": true, - "description": "The tag of the source image.", - "value": "3.7" - }, - { - "name": "OUTPUT_IMAGE_TAG", - "displayName": "Output Image Tag", - "description": "The tag given to the built image.", - "required": true, - "value": "latest" - }, - { - "name": "DOCKER_FILE_PATH", - "displayName": "Docker File Path", - "description": "The path to the docker file defining the build.", - "required": false, - "value": "Dockerfile" - } - ] -} diff --git a/jobs/ftp-poller/openshift/ftp-poller-deploy.json b/jobs/ftp-poller/openshift/ftp-poller-deploy.json deleted file mode 100644 index 87dd37b9e..000000000 --- a/jobs/ftp-poller/openshift/ftp-poller-deploy.json +++ /dev/null @@ -1,417 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for a ftp poller job.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%" - } - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "${IMAGE_NAMESPACE}", - "name": "${NAME}:${TAG_NAME}" - } - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": 1, - "test": false, - "selector": { - "app": "${NAME}-${TAG_NAME}", - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "template": { - "metadata": { - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "deploymentconfig": "${NAME}-${TAG_NAME}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "volumes": [ - { - "name": "cron-config", - "configMap": { - "name": "${NAME}-${TAG_NAME}-cron-configuration", - "defaultMode": 420 - } - }, - { - "name": "sftp-private-key", - "configMap": { - "name": "${NAME}-${TAG_NAME}-sftp-configuration", - "defaultMode": 420 - } - } - ], - "containers": [ - { - "name": "${NAME}-${TAG_NAME}", - "image": "docker-registry.default.svc:5000/${IMAGE_NAMESPACE}/${NAME}:${TAG_NAME}", - "ports": [ - { - "containerPort": 8080, - "protocol": "TCP" - } - ], - "volumeMounts": [ - { - "name": "cron-config", - "readOnly": true, - "mountPath": "/ftp-poller/cron/" - }, - { - "name": "sftp-private-key", - "readOnly": true, - "mountPath": "/ftp-poller/key/" - } - ], - "env": [ - { - "name": "DATABASE_USERNAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_USER" - } - } - }, - { - "name": "DATABASE_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-secret", - "key": "DATABASE_PASSWORD" - } - } - }, - { - "name": "DATABASE_NAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_NAME" - } - } - }, - { - "name": "DATABASE_HOST", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_HOST" - } - } - }, - { - "name": "DATABASE_PORT", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_PORT" - } - } - }, - { - "name": "DATABASE_TEST_USERNAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_USER" - } - } - }, - { - "name": "DATABASE_TEST_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-secret", - "key": "DATABASE_TEST_PASSWORD" - } - } - }, - { - "name": "DATABASE_TEST_NAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_NAME" - } - } - }, - { - "name": "DATABASE_TEST_HOST", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_HOST" - } - } - }, - { - "name": "DATABASE_TEST_PORT", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_PORT" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_REF_NUMBER", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_REF_NUMBER" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_API_KEY", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_API_KEY" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_BASE_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_BASE_URL" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_CLIENT_ID", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_CLIENT_ID" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_CLIENT_SECRET", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_CLIENT_SECRET" - } - } - }, - { - "name": "NOTIFY_API_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "NOTIFY_API_URL" - } - } - }, - { - "name": "KEYCLOAK_SERVICE_ACCOUNT_ID", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "KEYCLOAK_SERVICE_ACCOUNT_ID" - } - } - }, - { - "name": "KEYCLOAK_SERVICE_ACCOUNT_SECRET", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "KEYCLOAK_SERVICE_ACCOUNT_SECRET" - } - } - }, - { - "name": "JWT_OIDC_ISSUER", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "JWT_OIDC_ISSUER" - } - } - }, - { - "name": "AUTH_WEB_PAY_TRANSACTION_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "AUTH_WEB_PAY_TRANSACTION_URL" - } - } - } - ], - "resources": { - "requests": { - "cpu": "${CPU_REQUEST}", - "memory": "${MEMORY_REQUEST}" - }, - "limits": { - "cpu": "${CPU_LIMIT}", - "memory": "${MEMORY_LIMIT}" - } - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - } - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "creationTimestamp": null, - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "ports": [ - { - "name": "8080-tcp", - "protocol": "TCP", - "port": 8080, - "targetPort": 8080 - } - ], - "selector": { - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "ftp-poller" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - } - ] -} \ No newline at end of file diff --git a/jobs/ftp-poller/poetry.lock b/jobs/ftp-poller/poetry.lock new file mode 100644 index 000000000..befc94f7c --- /dev/null +++ b/jobs/ftp-poller/poetry.lock @@ -0,0 +1,2697 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + +[[package]] +name = "aniso8601" +version = "9.0.1" +description = "A library for parsing ISO 8601 strings." +optional = false +python-versions = "*" +files = [ + {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"}, + {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"}, +] + +[package.extras] +dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"] + +[[package]] +name = "argon2-cffi" +version = "23.1.0" +description = "Argon2 for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, + {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, +] + +[package.dependencies] +argon2-cffi-bindings = "*" + +[package.extras] +dev = ["argon2-cffi[tests,typing]", "tox (>4)"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"] +tests = ["hypothesis", "pytest"] +typing = ["mypy"] + +[[package]] +name = "argon2-cffi-bindings" +version = "21.2.0" +description = "Low-level CFFI bindings for Argon2" +optional = false +python-versions = ">=3.6" +files = [ + {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, + {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, +] + +[package.dependencies] +cffi = ">=1.0.1" + +[package.extras] +dev = ["cogapp", "pre-commit", "pytest", "wheel"] +tests = ["pytest"] + +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + +[[package]] +name = "astroid" +version = "3.2.2" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, + {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "autopep8" +version = "2.2.0" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.8" +files = [ + {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"}, + {file = "autopep8-2.2.0.tar.gz", hash = "sha256:d306a0581163ac29908280ad557773a95a9bede072c0fafed6f141f5311f43c1"}, +] + +[package.dependencies] +pycodestyle = ">=2.11.0" + +[[package]] +name = "bcrypt" +version = "4.1.3" +description = "Modern password hashing for your software and your servers" +optional = false +python-versions = ">=3.7" +files = [ + {file = "bcrypt-4.1.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:48429c83292b57bf4af6ab75809f8f4daf52aa5d480632e53707805cc1ce9b74"}, + {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a8bea4c152b91fd8319fef4c6a790da5c07840421c2b785084989bf8bbb7455"}, + {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d3b317050a9a711a5c7214bf04e28333cf528e0ed0ec9a4e55ba628d0f07c1a"}, + {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:094fd31e08c2b102a14880ee5b3d09913ecf334cd604af27e1013c76831f7b05"}, + {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4fb253d65da30d9269e0a6f4b0de32bd657a0208a6f4e43d3e645774fb5457f3"}, + {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:193bb49eeeb9c1e2db9ba65d09dc6384edd5608d9d672b4125e9320af9153a15"}, + {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:8cbb119267068c2581ae38790e0d1fbae65d0725247a930fc9900c285d95725d"}, + {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6cac78a8d42f9d120b3987f82252bdbeb7e6e900a5e1ba37f6be6fe4e3848286"}, + {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01746eb2c4299dd0ae1670234bf77704f581dd72cc180f444bfe74eb80495b64"}, + {file = "bcrypt-4.1.3-cp37-abi3-win32.whl", hash = "sha256:037c5bf7c196a63dcce75545c8874610c600809d5d82c305dd327cd4969995bf"}, + {file = "bcrypt-4.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:8a893d192dfb7c8e883c4576813bf18bb9d59e2cfd88b68b725990f033f1b978"}, + {file = "bcrypt-4.1.3-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d4cf6ef1525f79255ef048b3489602868c47aea61f375377f0d00514fe4a78c"}, + {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5698ce5292a4e4b9e5861f7e53b1d89242ad39d54c3da451a93cac17b61921a"}, + {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec3c2e1ca3e5c4b9edb94290b356d082b721f3f50758bce7cce11d8a7c89ce84"}, + {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3a5be252fef513363fe281bafc596c31b552cf81d04c5085bc5dac29670faa08"}, + {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5f7cd3399fbc4ec290378b541b0cf3d4398e4737a65d0f938c7c0f9d5e686611"}, + {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:c4c8d9b3e97209dd7111bf726e79f638ad9224b4691d1c7cfefa571a09b1b2d6"}, + {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:31adb9cbb8737a581a843e13df22ffb7c84638342de3708a98d5c986770f2834"}, + {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:551b320396e1d05e49cc18dd77d970accd52b322441628aca04801bbd1d52a73"}, + {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6717543d2c110a155e6821ce5670c1f512f602eabb77dba95717ca76af79867d"}, + {file = "bcrypt-4.1.3-cp39-abi3-win32.whl", hash = "sha256:6004f5229b50f8493c49232b8e75726b568535fd300e5039e255d919fc3a07f2"}, + {file = "bcrypt-4.1.3-cp39-abi3-win_amd64.whl", hash = "sha256:2505b54afb074627111b5a8dc9b6ae69d0f01fea65c2fcaea403448c503d3991"}, + {file = "bcrypt-4.1.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:cb9c707c10bddaf9e5ba7cdb769f3e889e60b7d4fea22834b261f51ca2b89fed"}, + {file = "bcrypt-4.1.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9f8ea645eb94fb6e7bea0cf4ba121c07a3a182ac52876493870033141aa687bc"}, + {file = "bcrypt-4.1.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f44a97780677e7ac0ca393bd7982b19dbbd8d7228c1afe10b128fd9550eef5f1"}, + {file = "bcrypt-4.1.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d84702adb8f2798d813b17d8187d27076cca3cd52fe3686bb07a9083930ce650"}, + {file = "bcrypt-4.1.3.tar.gz", hash = "sha256:2ee15dd749f5952fe3f0430d0ff6b74082e159c50332a1413d51b5689cf06623"}, +] + +[package.extras] +tests = ["pytest (>=3.2.1,!=3.3.0)"] +typecheck = ["mypy"] + +[[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 = "cachelib" +version = "0.9.0" +description = "A collection of cache libraries in the same API interface." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachelib-0.9.0-py3-none-any.whl", hash = "sha256:811ceeb1209d2fe51cd2b62810bd1eccf70feba5c52641532498be5c675493b3"}, + {file = "cachelib-0.9.0.tar.gz", hash = "sha256:38222cc7c1b79a23606de5c2607f4925779e37cdcea1c2ad21b8bae94b5425a5"}, +] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "cattrs" +version = "23.2.3" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"}, + {file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"}, +] + +[package.dependencies] +attrs = ">=23.1.0" + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +orjson = ["orjson (>=3.9.2)"] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.5.3" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "croniter" +version = "2.0.2" +description = "croniter provides iteration for datetime object with cron like format" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "croniter-2.0.2-py2.py3-none-any.whl", hash = "sha256:78bf110a2c7dbbfdd98b926318ae6c64a731a4c637c7befe3685755110834746"}, + {file = "croniter-2.0.2.tar.gz", hash = "sha256:8bff16c9af4ef1fb6f05416973b8f7cb54997c02f2f8365251f9bf1dded91866"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = ">2021.1" + +[[package]] +name = "cryptography" +version = "42.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "dpath" +version = "2.1.6" +description = "Filesystem-like pathing and searching for dictionaries" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dpath-2.1.6-py3-none-any.whl", hash = "sha256:31407395b177ab63ef72e2f6ae268c15e938f2990a8ecf6510f5686c02b6db73"}, + {file = "dpath-2.1.6.tar.gz", hash = "sha256:f1e07c72e8605c6a9e80b64bc8f42714de08a789c7de417e49c3f87a19692e47"}, +] + +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "expiringdict" +version = "1.2.2" +description = "Dictionary with auto-expiring values for caching purposes" +optional = false +python-versions = "*" +files = [ + {file = "expiringdict-1.2.2-py3-none-any.whl", hash = "sha256:09a5d20bc361163e6432a874edd3179676e935eb81b925eccef48d409a8a45e8"}, + {file = "expiringdict-1.2.2.tar.gz", hash = "sha256:300fb92a7e98f15b05cf9a856c1415b3bc4f2e132be07daa326da6414c23ee09"}, +] + +[package.extras] +tests = ["coverage", "coveralls", "dill", "mock", "nose"] + +[[package]] +name = "flake8" +version = "7.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "flake8-blind-except" +version = "0.2.1" +description = "A flake8 extension that checks for blind except: statements" +optional = false +python-versions = "*" +files = [ + {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, +] + +[[package]] +name = "flake8-debugger" +version = "4.1.2" +description = "ipdb/pdb statement checker plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-isort" +version = "6.1.1" +description = "flake8 plugin that integrates isort" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_isort-6.1.1-py3-none-any.whl", hash = "sha256:0fec4dc3a15aefbdbe4012e51d5531a2eb5fa8b981cdfbc882296a59b54ede12"}, + {file = "flake8_isort-6.1.1.tar.gz", hash = "sha256:c1f82f3cf06a80c13e1d09bfae460e9666255d5c780b859f19f8318d420370b3"}, +] + +[package.dependencies] +flake8 = "*" +isort = ">=5.0.0,<6" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "flake8-quotes" +version = "3.4.0" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, +] + +[package.dependencies] +flake8 = "*" +setuptools = "*" + +[[package]] +name = "flask" +version = "3.0.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, +] + +[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 = "flask-caching" +version = "2.3.0" +description = "Adds caching support to Flask applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, + {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, +] + +[package.dependencies] +cachelib = ">=0.9.0,<0.10.0" +Flask = "*" + +[[package]] +name = "flask-cors" +version = "4.0.0" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Cors-4.0.0.tar.gz", hash = "sha256:f268522fcb2f73e2ecdde1ef45e2fd5c71cc48fe03cffb4b441c6d1b40684eb0"}, + {file = "Flask_Cors-4.0.0-py2.py3-none-any.whl", hash = "sha256:bc3492bfd6368d27cfe79c7821df5a8a319e1a6d5eab277a3794be19bdc51783"}, +] + +[package.dependencies] +Flask = ">=0.9" + +[[package]] +name = "flask-jwt-oidc" +version = "0.7.0" +description = "Opinionated flask oidc client" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +cachelib = "0.*" +Flask = ">=2" +python-jose = "^3.3.0" +six = "^1.16.0" + +[package.source] +type = "git" +url = "https://github.com/seeker25/flask-jwt-oidc.git" +reference = "HEAD" +resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" + +[[package]] +name = "flask-marshmallow" +version = "1.2.0" +description = "Flask + marshmallow for beautiful APIs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_marshmallow-1.2.0-py3-none-any.whl", hash = "sha256:ddd2a7c8db5e00a8d56c8ca5f651efae1de7d76b7d821b56ccc2caf09135ad12"}, + {file = "flask_marshmallow-1.2.0.tar.gz", hash = "sha256:d0f79eb9743f0c530a3d9e848503e1f2228e6b35a819c91e913af02e68421805"}, +] + +[package.dependencies] +Flask = ">=2.2" +marshmallow = ">=3.0.0" + +[package.extras] +dev = ["flask-marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["Sphinx (==7.2.6)", "marshmallow-sqlalchemy (>=0.19.0)", "sphinx-issues (==4.0.0)"] +sqlalchemy = ["flask-sqlalchemy (>=3.0.0)", "marshmallow-sqlalchemy (>=0.29.0)"] +tests = ["flask-marshmallow[sqlalchemy]", "pytest"] + +[[package]] +name = "flask-migrate" +version = "4.0.7" +description = "SQLAlchemy database migrations for Flask applications using Alembic." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Migrate-4.0.7.tar.gz", hash = "sha256:dff7dd25113c210b069af280ea713b883f3840c1e3455274745d7355778c8622"}, + {file = "Flask_Migrate-4.0.7-py3-none-any.whl", hash = "sha256:5c532be17e7b43a223b7500d620edae33795df27c75811ddf32560f7d48ec617"}, +] + +[package.dependencies] +alembic = ">=1.9.0" +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" + +[[package]] +name = "flask-moment" +version = "1.0.5" +description = "Formatting of dates and times in Flask templates using moment.js." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Moment-1.0.5.tar.gz", hash = "sha256:33307ecd4af8290b6df6a9828ff55ac0977d0567817f9bc0f69803d22ed2b55c"}, + {file = "Flask_Moment-1.0.5-py3-none-any.whl", hash = "sha256:6e7b3eef89e2137bbbee975405f241a68a44edfa34bf052c92d84364992adca6"}, +] + +[package.dependencies] +Flask = "*" +packaging = ">=14.1" + +[[package]] +name = "flask-opentracing" +version = "1.1.0" +description = "OpenTracing support for Flask applications" +optional = false +python-versions = "*" +files = [ + {file = "Flask-OpenTracing-1.1.0.tar.gz", hash = "sha256:a9a39d367fbe7e9ed9c77b90ac48159c1a3e82982a5abf84d3f4d710d24580ac"}, +] + +[package.dependencies] +Flask = "*" +opentracing = ">=2.0,<3" + +[package.extras] +tests = ["flake8", "flake8-quotes", "mock", "pytest", "pytest-cov"] + +[[package]] +name = "flask-restplus" +version = "0.13.0" +description = "Fully featured framework for fast, easy and documented API development with Flask" +optional = false +python-versions = "*" +files = [ + {file = "flask-restplus-0.13.0.tar.gz", hash = "sha256:a66e442d0bca08f389fc3d07b4d808fc89961285d12fb8013f7cf15516fa9f5c"}, + {file = "flask_restplus-0.13.0-py2.py3-none-any.whl", hash = "sha256:a15d251923a8feb09a5d805c2f4d188555910a42c64d58f7dd281b8cac095f1b"}, +] + +[package.dependencies] +aniso8601 = ">=0.82" +Flask = ">=0.8" +jsonschema = "*" +pytz = "*" +six = ">=1.3.0" + +[package.extras] +dev = ["blinker", "flake8 (==3.5.0)", "invoke (==0.21.0)", "mock (==2.0.0)", "pytest (==3.2.3)", "pytest-benchmark (==3.1.1)", "pytest-cov (==2.5.1)", "pytest-faker (==2.0.0)", "pytest-flask (==0.10.0)", "pytest-mock (==1.6.3)", "pytest-profiling (==1.2.11)", "pytest-sugar (==0.9.0)", "readme-renderer (==17.2)", "tox (==2.9.1)", "tzlocal"] +doc = ["Sphinx (==2.0.1)", "alabaster (==0.7.10)", "sphinx-issues (==1.2.0)"] +test = ["blinker", "mock (==2.0.0)", "pytest (==3.2.3)", "pytest-benchmark (==3.1.1)", "pytest-cov (==2.5.1)", "pytest-faker (==2.0.0)", "pytest-flask (==0.10.0)", "pytest-mock (==1.6.3)", "pytest-profiling (==1.2.11)", "pytest-sugar (==0.9.0)", "tzlocal"] + +[[package]] +name = "flask-script" +version = "2.0.6" +description = "Scripting support for Flask" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Script-2.0.6.tar.gz", hash = "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65"}, +] + +[package.dependencies] +Flask = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +description = "Add SQLAlchemy support to your Flask application." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[package.dependencies] +flask = ">=2.2.5" +sqlalchemy = ">=2.0.16" + +[[package]] +name = "gcp-queue" +version = "0.3.0" +description = "" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +flask = ">=1" +google-auth = "^2.28.2" +google-cloud-pubsub = "^2.20.2" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-connect-common.git" +reference = "main" +resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" +subdirectory = "python/gcp-queue" + +[[package]] +name = "google-api-core" +version = "2.19.0" +description = "Google API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, + {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +proto-plus = ">=1.22.3,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-auth" +version = "2.29.0" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-cloud-pubsub" +version = "2.21.2" +description = "Google Cloud Pub/Sub API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, + {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<3.0.0dev" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +grpcio = ">=1.51.3,<2.0dev" +grpcio-status = ">=1.33.2" +proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +libcst = ["libcst (>=0.3.10)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.63.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, + {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.13.0" +description = "IAM API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.13.0.tar.gz", hash = "sha256:fad318608b9e093258fbf12529180f400d1c44453698a33509cc6ecf005b294e"}, + {file = "grpc_google_iam_v1-0.13.0-py2.py3-none-any.whl", hash = "sha256:53902e2af7de8df8c1bd91373d9be55b0743ec267a7428ea638db3775becae89"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.64.0" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.8" +files = [ + {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, + {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, + {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, + {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, + {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, + {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, + {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, + {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, + {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, + {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, + {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, + {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, + {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, + {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, + {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, + {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, + {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, + {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, + {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, + {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, + {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.64.0)"] + +[[package]] +name = "grpcio-status" +version = "1.62.2" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, + {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.62.2" +protobuf = ">=4.21.6" + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "holidays" +version = "0.37" +description = "Generate and work with holidays in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "holidays-0.37-py3-none-any.whl", hash = "sha256:5b8ff8c94c06e3b225762d495e51b8e51205d332f8ad092aab809c4bffa8d123"}, + {file = "holidays-0.37.tar.gz", hash = "sha256:712df71a8d97b04554fa1c9208d219fbf174bad2864263bef24c6dcfa1ded6ff"}, +] + +[package.dependencies] +python-dateutil = "*" + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[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 = "jaeger-client" +version = "4.8.0" +description = "Jaeger Python OpenTracing Tracer implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaeger-client-4.8.0.tar.gz", hash = "sha256:3157836edab8e2c209bd2d6ae61113db36f7ee399e66b1dcbb715d87ab49bfe0"}, +] + +[package.dependencies] +opentracing = ">=2.1,<3.0" +threadloop = ">=1,<2" +thrift = "*" +tornado = ">=4.3" + +[package.extras] +tests = ["codecov", "coverage", "flake8", "flake8-quotes", "flake8-typing-imports", "mock", "mypy", "opentracing_instrumentation (>=3,<4)", "prometheus_client (==0.11.0)", "pycurl", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "pytest-localserver", "pytest-timeout", "pytest-tornado", "tchannel (==2.1.0)"] + +[[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 = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "launchdarkly-eventsource" +version = "1.1.1" +description = "LaunchDarkly SSE Client" +optional = false +python-versions = ">=3.7" +files = [ + {file = "launchdarkly_eventsource-1.1.1-py3-none-any.whl", hash = "sha256:3d7e5301bc4b4a744ecdaa10de8bce52c2f3c66a97e9aa10ab11ca81b67fb31b"}, + {file = "launchdarkly_eventsource-1.1.1.tar.gz", hash = "sha256:211791f1267f9b7b0a62a0bb5fc9c5ed1fb4a834440f16be551968dbe772557a"}, +] + +[package.dependencies] +urllib3 = ">=1.26.0,<3" + +[[package]] +name = "launchdarkly-server-sdk" +version = "8.2.1" +description = "LaunchDarkly SDK for Python" +optional = false +python-versions = "*" +files = [ + {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, + {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, +] + +[package.dependencies] +certifi = ">=2018.4.16" +expiringdict = ">=1.1.4" +pyRFC3339 = ">=1.0" +semver = ">=2.10.2" +urllib3 = ">=1.22.0,<3" + +[package.extras] +consul = ["python-consul (>=1.0.1)"] +dynamodb = ["boto3 (>=1.9.71)"] +redis = ["redis (>=2.10.5)"] + +[[package]] +name = "lovely-pytest-docker" +version = "0.3.1" +description = "Pytest testing utilities with docker containers." +optional = false +python-versions = "*" +files = [ + {file = "lovely-pytest-docker-0.3.1.tar.gz", hash = "sha256:4326a180bfd4dd4ad69c2ef3e3643c41075d965f40068488b40204602e6df85e"}, +] + +[package.dependencies] +pytest = "*" +six = "*" + +[[package]] +name = "mako" +version = "1.3.2" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, + {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "marshmallow" +version = "3.21.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-sqlalchemy" +version = "1.0.0" +description = "SQLAlchemy integration with the marshmallow (de)serialization library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow_sqlalchemy-1.0.0-py3-none-any.whl", hash = "sha256:f415d57809e3555b6323356589aba91e36e4470f35953d3a10c755ac5c3307df"}, + {file = "marshmallow_sqlalchemy-1.0.0.tar.gz", hash = "sha256:20a0f2fcdd5bddc86444fa01461f17f9b6a12a8ddd4ca8c9b34fe2f2e35d00a2"}, +] + +[package.dependencies] +marshmallow = ">=3.10.0" +SQLAlchemy = ">=1.4.40,<3.0" + +[package.extras] +dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)"] +tests = ["pytest (<8)", "pytest-lazy-fixture (>=0.6.2)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "minio" +version = "7.2.7" +description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" +optional = false +python-versions = "*" +files = [ + {file = "minio-7.2.7-py3-none-any.whl", hash = "sha256:59d1f255d852fe7104018db75b3bebbd987e538690e680f7c5de835e422de837"}, + {file = "minio-7.2.7.tar.gz", hash = "sha256:473d5d53d79f340f3cd632054d0c82d2f93177ce1af2eac34a235bea55708d98"}, +] + +[package.dependencies] +argon2-cffi = "*" +certifi = "*" +pycryptodome = "*" +typing-extensions = "*" +urllib3 = "*" + +[[package]] +name = "opentracing" +version = "2.4.0" +description = "OpenTracing API for Python. See documentation at http://opentracing.io" +optional = false +python-versions = "*" +files = [ + {file = "opentracing-2.4.0.tar.gz", hash = "sha256:a173117e6ef580d55874734d1fa7ecb6f3655160b8b8974a2a1e98e5ec9c840d"}, +] + +[package.extras] +tests = ["Sphinx", "doubles", "flake8", "flake8-quotes", "gevent", "mock", "pytest", "pytest-cov", "pytest-mock", "six (>=1.10.0,<2.0)", "sphinx_rtd_theme", "tornado"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "paramiko" +version = "3.4.0" +description = "SSH2 protocol library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "paramiko-3.4.0-py3-none-any.whl", hash = "sha256:43f0b51115a896f9c00f59618023484cb3a14b98bbceab43394a39c6739b7ee7"}, + {file = "paramiko-3.4.0.tar.gz", hash = "sha256:aac08f26a31dc4dffd92821527d1682d99d52f9ef6851968114a8728f3c274d3"}, +] + +[package.dependencies] +bcrypt = ">=3.2" +cryptography = ">=3.3" +pynacl = ">=1.5" + +[package.extras] +all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +invoke = ["invoke (>=2.0)"] + +[[package]] +name = "pay-api" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.12" +files = [] +develop = false + +[package.dependencies] +alembic = "1.13.1" +attrs = "23.2.0" +blinker = "1.7.0" +cachelib = "0.9.0" +cachetools = "5.3.3" +cattrs = "23.2.3" +certifi = "2024.2.2" +cffi = "1.16.0" +charset-normalizer = "3.3.2" +click = "8.1.7" +croniter = "2.0.2" +cryptography = "42.0.5" +dpath = "2.1.6" +ecdsa = "0.18.0" +expiringdict = "1.2.2" +flask = "3.0.2" +flask-caching = "2.3.0" +flask-cors = "4.0.0" +flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} +flask-marshmallow = "1.2.0" +flask-migrate = "4.0.7" +flask-moment = "1.0.5" +flask-script = "2.0.6" +flask-sqlalchemy = "3.1.1" +gcp-queue = {git = "https://github.com/seeker25/sbc-connect-common.git", branch = "main", subdirectory = "python/gcp-queue"} +greenlet = "3.0.3" +gunicorn = "21.2.0" +holidays = "0.37" +idna = "3.6" +itsdangerous = "2.1.2" +jaeger-client = "4.8.0" +jinja2 = "3.1.3" +jsonschema = "4.17.3" +launchdarkly-eventsource = "1.1.1" +launchdarkly-server-sdk = "8.2.1" +mako = "1.3.2" +markupsafe = "2.1.5" +marshmallow = "3.21.1" +marshmallow-sqlalchemy = "1.0.0" +opentracing = "2.4.0" +packaging = "24.0" +pg8000 = "^1.30.5" +proto-plus = "1.23.0" +protobuf = "4.25.3" +psycopg2-binary = "2.9.9" +pyasn1 = "0.5.1" +pyasn1-modules = "0.3.0" +pycparser = "2.21" +pyhumps = "3.8.0" +pyrfc3339 = "1.1" +pyrsistent = "0.20.0" +python-dateutil = "2.9.0.post0" +python-dotenv = "1.0.1" +python-jose = "3.3.0" +pytz = "2024.1" +requests = "2.31.0" +rsa = "4.9" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +semver = "3.0.2" +sentry-sdk = "1.41.0" +six = "1.16.0" +sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} +sqlalchemy = "2.0.28" +sqlalchemy-utils = "0.41.1" +threadloop = "1.0.2" +thrift = "0.16.0" +tornado = "6.4" +typing-extensions = "4.10.0" +urllib3 = "2.2.1" +werkzeug = "3.0.1" + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-pay.git" +reference = "sync-python-to-main" +resolved_reference = "522ed4997b03f8bb5d88864e739815548aa606be" +subdirectory = "pay-api" + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "pg8000" +version = "1.31.2" +description = "PostgreSQL interface library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, + {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.2" +scramp = ">=1.4.5" + +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "proto-plus" +version = "1.23.0" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.23.0.tar.gz", hash = "sha256:89075171ef11988b3fa157f5dbd8b9cf09d65fffee97e29ce403cd8defba19d2"}, + {file = "proto_plus-1.23.0-py3-none-any.whl", hash = "sha256:a829c79e619e1cf632de091013a4173deed13a55f326ef84f05af6f50ff4c82c"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + +[[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 = "pyasn1" +version = "0.5.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pycryptodome" +version = "3.20.0" +description = "Cryptographic library for Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e"}, + {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pyhamcrest" +version = "2.1.0" +description = "Hamcrest framework for matcher objects" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyhamcrest-2.1.0-py3-none-any.whl", hash = "sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587"}, + {file = "pyhamcrest-2.1.0.tar.gz", hash = "sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c"}, +] + +[package.extras] +dev = ["black", "doc2dash", "flake8", "pyhamcrest[docs,tests]", "pytest-mypy", "towncrier", "tox", "tox-asdf", "twine"] +docs = ["alabaster (>=0.7,<1.0)", "sphinx (>=4.0,<5.0)"] +tests = ["coverage[toml]", "dataclasses", "mypy (!=0.940)", "pytest (>=5.0)", "pytest-mypy-plugins", "pytest-sugar", "pytest-xdist", "pyyaml", "types-dataclasses", "types-mock"] +tests-numpy = ["numpy", "pyhamcrest[tests]"] + +[[package]] +name = "pyhumps" +version = "3.8.0" +description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" +optional = false +python-versions = "*" +files = [ + {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, + {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, +] + +[[package]] +name = "pylint" +version = "3.2.2" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, + {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, +] + +[package.dependencies] +astroid = ">=3.2.2,<=3.3.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint-flask" +version = "0.6" +description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" +optional = false +python-versions = "*" +files = [ + {file = "pylint-flask-0.6.tar.gz", hash = "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517"}, +] + +[package.dependencies] +pylint-plugin-utils = ">=0.2.1" + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + +[[package]] +name = "pynacl" +version = "1.5.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, +] + +[package.dependencies] +cffi = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] + +[[package]] +name = "pyrfc3339" +version = "1.1" +description = "Generate and parse RFC 3339 timestamps" +optional = false +python-versions = "*" +files = [ + {file = "pyRFC3339-1.1-py2.py3-none-any.whl", hash = "sha256:67196cb83b470709c580bb4738b83165e67c6cc60e1f2e4f286cfcb402a926f4"}, + {file = "pyRFC3339-1.1.tar.gz", hash = "sha256:81b8cbe1519cdb79bed04910dd6fa4e181faf8c88dff1e1b987b5f7ab23a5b1a"}, +] + +[package.dependencies] +pytz = "*" + +[[package]] +name = "pyrsistent" +version = "0.20.0" +description = "Persistent/Functional/Immutable data structures" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, + {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, + {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, + {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, + {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, + {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, + {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, +] + +[[package]] +name = "pysftp" +version = "0.2.9" +description = "A friendly face on SFTP" +optional = false +python-versions = "*" +files = [ + {file = "pysftp-0.2.9.tar.gz", hash = "sha256:fbf55a802e74d663673400acd92d5373c1c7ee94d765b428d9f977567ac4854a"}, +] + +[package.dependencies] +paramiko = ">=1.17" + +[[package]] +name = "pytest" +version = "8.2.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, + {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.23.7" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_asyncio-0.23.7-py3-none-any.whl", hash = "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b"}, + {file = "pytest_asyncio-0.23.7.tar.gz", hash = "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268"}, +] + +[package.dependencies] +pytest = ">=7.0.0,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, +] + +[package.dependencies] +pytest = ">=6.2.5" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +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.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "sbc_common_components" +version = "0.0.0" +description = "" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +flask = "*" +flask-jwt-oidc = ">=0.1.5" +Flask-OpenTracing = "1.1.0" +Flask-SQLAlchemy = "*" +jaeger-client = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-common-components.git" +reference = "HEAD" +resolved_reference = "e770b4ab496e044d292500e62bc19a17079a73ec" +subdirectory = "python" + +[[package]] +name = "scramp" +version = "1.4.5" +description = "An implementation of the SCRAM protocol." +optional = false +python-versions = ">=3.8" +files = [ + {file = "scramp-1.4.5-py3-none-any.whl", hash = "sha256:50e37c464fc67f37994e35bee4151e3d8f9320e9c204fca83a5d313c121bbbe7"}, + {file = "scramp-1.4.5.tar.gz", hash = "sha256:be3fbe774ca577a7a658117dca014e5d254d158cecae3dd60332dfe33ce6d78e"}, +] + +[package.dependencies] +asn1crypto = ">=1.5.1" + +[[package]] +name = "semver" +version = "3.0.2" +description = "Python helper for Semantic Versioning (https://semver.org)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "semver-3.0.2-py3-none-any.whl", hash = "sha256:b1ea4686fe70b981f85359eda33199d60c53964284e0cfb4977d243e37cf4bf4"}, + {file = "semver-3.0.2.tar.gz", hash = "sha256:6253adb39c70f6e51afed2fa7152bcd414c411286088fb4b9effb133885ab4cc"}, +] + +[[package]] +name = "sentry-sdk" +version = "1.41.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.41.0.tar.gz", hash = "sha256:4f2d6c43c07925d8cd10dfbd0970ea7cb784f70e79523cca9dbcd72df38e5a46"}, + {file = "sentry_sdk-1.41.0-py2.py3-none-any.whl", hash = "sha256:be4f8f4b29a80b6a3b71f0f31487beb9e296391da20af8504498a328befed53f"}, +] + +[package.dependencies] +certifi = "*" +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "70.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "simple-cloudevent" +version = "0.0.2" +description = "A short description of the project" +optional = false +python-versions = ">=3.8" +files = [] +develop = false + +[package.dependencies] +strict-rfc3339 = "*" + +[package.source] +type = "git" +url = "https://github.com/daxiom/simple-cloudevent.py.git" +reference = "HEAD" +resolved_reference = "447cabb988202206ac69e71177d7cd11b6c0b002" + +[[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 = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sql-versioning" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.10" +files = [] +develop = false + +[package.source] +type = "git" +url = "https://github.com/bcgov/lear.git" +reference = "feature-legal-name" +resolved_reference = "e5a432d1460dc84208465ef35c0c81ab02e66f51" +subdirectory = "python/common/sql-versioning" + +[[package]] +name = "sqlalchemy" +version = "2.0.28" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "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\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlalchemy-utils" +version = "0.41.1" +description = "Various utility functions for SQLAlchemy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "SQLAlchemy-Utils-0.41.1.tar.gz", hash = "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74"}, + {file = "SQLAlchemy_Utils-0.41.1-py3-none-any.whl", hash = "sha256:6c96b0768ea3f15c0dc56b363d386138c562752b84f647fb8d31a2223aaab801"}, +] + +[package.dependencies] +SQLAlchemy = ">=1.3" + +[package.extras] +arrow = ["arrow (>=0.3.4)"] +babel = ["Babel (>=1.3)"] +color = ["colour (>=0.0.4)"] +encrypted = ["cryptography (>=0.6)"] +intervals = ["intervals (>=0.7.1)"] +password = ["passlib (>=1.6,<2.0)"] +pendulum = ["pendulum (>=2.0.5)"] +phone = ["phonenumbers (>=5.9.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +timezone = ["python-dateutil"] +url = ["furl (>=0.4.1)"] + +[[package]] +name = "strict-rfc3339" +version = "0.7" +description = "Strict, simple, lightweight RFC3339 functions" +optional = false +python-versions = "*" +files = [ + {file = "strict-rfc3339-0.7.tar.gz", hash = "sha256:5cad17bedfc3af57b399db0fed32771f18fc54bbd917e85546088607ac5e1277"}, +] + +[[package]] +name = "threadloop" +version = "1.0.2" +description = "Tornado IOLoop Backed Concurrent Futures" +optional = false +python-versions = "*" +files = [ + {file = "threadloop-1.0.2-py2-none-any.whl", hash = "sha256:5c90dbefab6ffbdba26afb4829d2a9df8275d13ac7dc58dccb0e279992679599"}, + {file = "threadloop-1.0.2.tar.gz", hash = "sha256:8b180aac31013de13c2ad5c834819771992d350267bddb854613ae77ef571944"}, +] + +[package.dependencies] +tornado = "*" + +[[package]] +name = "thrift" +version = "0.16.0" +description = "Python bindings for the Apache Thrift RPC system" +optional = false +python-versions = "*" +files = [ + {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, +] + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + +[[package]] +name = "tomlkit" +version = "0.12.5" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[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.12" +content-hash = "3aeb528030f868f3ab60ec2d31aab0dd38f09872fe7ba62082c45715aaeed206" diff --git a/jobs/ftp-poller/pyproject.toml b/jobs/ftp-poller/pyproject.toml new file mode 100644 index 000000000..34ecaccca --- /dev/null +++ b/jobs/ftp-poller/pyproject.toml @@ -0,0 +1,51 @@ +[tool.poetry] +name = "ftp-poller" +version = "0.1.0" +description = "" +authors = ["Travis Semple "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +gunicorn = "^21.2.0" +flask = "^3.0.2" +flask-restplus = "^0.13.0" +python-dotenv = "^1.0.1" +psycopg2-binary = "^2.9.9" +jsonschema = "4.17.3" +requests = "^2.31.0" +werkzeug = "^3.0.1" +pysftp = "^0.2.9" +minio = "^7.2.5" +jaeger-client = "^4.8.0" +itsdangerous = "^2.1.2" +jinja2 = "^3.1.3" +protobuf = "4.25.3" +launchdarkly-server-sdk = "^8.2.1" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "sync-python-to-main", subdirectory = "pay-api"} + + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pytest-mock = "^3.12.0" +requests = "^2.31.0" +pyhamcrest = "^2.1.0" +pytest-cov = "^4.1.0" +flake8 = "^7.0.0" +flake8-blind-except = "^0.2.1" +flake8-debugger = "^4.1.2" +flake8-docstrings = "^1.7.0" +flake8-isort = "^6.1.1" +flake8-quotes = "^3.4.0" +pep8-naming = "^0.13.3" +autopep8 = "^2.0.4" +coverage = "^7.4.3" +pylint = "^3.1.0" +pylint-flask = "^0.6" +lovely-pytest-docker = "^0.3.1" +pytest-asyncio = "^0.23.5.post1" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/jobs/ftp-poller/requirements.txt b/jobs/ftp-poller/requirements.txt deleted file mode 100644 index 95817ce1e..000000000 --- a/jobs/ftp-poller/requirements.txt +++ /dev/null @@ -1,50 +0,0 @@ -Flask==1.1.2 -Jinja2==3.0.3 -MarkupSafe==2.1.5 -PyNaCl==1.5.0 -Werkzeug==1.0.1 -aniso8601==9.0.1 -argon2-cffi-bindings==21.2.0 -argon2-cffi==23.1.0 -attrs==23.2.0 -bcrypt==4.1.3 -certifi==2024.2.2 -cffi==1.16.0 -charset-normalizer==3.3.2 -click==8.1.7 -cryptography==42.0.7 -expiringdict==1.2.2 -flask-restplus==0.13.0 -gunicorn==22.0.0 -idna==3.7 -importlib_resources==6.4.0 -itsdangerous==2.0.1 -jaeger-client==4.8.0 -jsonschema==4.17.3 -launchdarkly-server-sdk==8.2.1 -minio==7.2.7 -opentracing==2.4.0 -packaging==24.0 -paramiko==3.4.0 -pkgutil_resolve_name==1.3.10 -protobuf==3.19.6 -psycopg2-binary==2.9.9 -pyRFC3339==1.1 -pycparser==2.22 -pycryptodome==3.20.0 -pyrsistent==0.20.0 -pysftp==0.2.9 -python-dotenv==1.0.1 -pytz==2024.1 -requests==2.32.2 -semver==3.0.2 -six==1.16.0 -threadloop==1.0.2 -thrift==0.20.0 -tornado==6.4 -typing_extensions==4.12.0 -urllib3==2.2.1 -zipp==3.19.0 --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/seeker25/sbc-pay.git@refactor_queues#egg=pay_api&subdirectory=pay-api --e git+https://github.com/seeker25/sbc-connect-common.git@small_tweaks#egg=gcp-queue&subdirectory=python/gcp-queue diff --git a/jobs/ftp-poller/requirements/dev.txt b/jobs/ftp-poller/requirements/dev.txt deleted file mode 100644 index 696e51010..000000000 --- a/jobs/ftp-poller/requirements/dev.txt +++ /dev/null @@ -1,27 +0,0 @@ -# Everything the developer needs in addition to the production requirements --r prod.txt - -# Testing -pytest -pytest-mock -requests -pyhamcrest -pytest-cov - -# Lint and code style -flake8==5.0.4 -flake8-blind-except -flake8-debugger -flake8-docstrings -flake8-isort -flake8-quotes -pep8-naming -autopep8 -coverage -pylint -pylint-flask -#attrs==19.1.0 - -# docker -lovely-pytest-docker -pytest-asyncio diff --git a/jobs/ftp-poller/requirements/prod.txt b/jobs/ftp-poller/requirements/prod.txt deleted file mode 100644 index 0a9d3b59c..000000000 --- a/jobs/ftp-poller/requirements/prod.txt +++ /dev/null @@ -1,15 +0,0 @@ -gunicorn -Flask -Flask-RESTplus -python-dotenv -psycopg2-binary -jsonschema==4.17.3 -requests -Werkzeug<2 -pysftp -minio -jaeger-client -itsdangerous==2.0.1 -Jinja2==3.0.3 -protobuf~=3.19.5 -launchdarkly-server-sdk==8.2.1 diff --git a/jobs/ftp-poller/requirements/repo-libraries.txt b/jobs/ftp-poller/requirements/repo-libraries.txt deleted file mode 100644 index a388c1fce..000000000 --- a/jobs/ftp-poller/requirements/repo-libraries.txt +++ /dev/null @@ -1,3 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/seeker25/sbc-pay.git@refactor_queues#egg=pay_api&subdirectory=pay-api --e git+https://github.com/seeker25/sbc-connect-common.git@small_tweaks#egg=gcp-queue&subdirectory=python/gcp-queue diff --git a/jobs/ftp-poller/setup.cfg b/jobs/ftp-poller/setup.cfg index 8eb46196f..c06c0dd9b 100755 --- a/jobs/ftp-poller/setup.cfg +++ b/jobs/ftp-poller/setup.cfg @@ -17,7 +17,7 @@ keywords = [options] zip_safe = True -python_requires = >=3.6 +python_requires = >=3.12 include_package_data = True packages = find: diff --git a/jobs/ftp-poller/tests/jobs/conftest.py b/jobs/ftp-poller/tests/jobs/conftest.py index d9023c6ba..fd4717049 100644 --- a/jobs/ftp-poller/tests/jobs/conftest.py +++ b/jobs/ftp-poller/tests/jobs/conftest.py @@ -14,93 +14,19 @@ """Common setup and fixtures for the py-test suite used by this service.""" -import sys import time import pytest -from flask_migrate import Migrate, upgrade -from pay_api.models import db as _db -from sqlalchemy import text -from sqlalchemy.schema import DropConstraint, MetaData - from invoke_jobs import create_app @pytest.fixture(scope='session') def app(): """Return a session-wide application configured in TEST mode.""" - _app = create_app('testing') - return _app - - -@pytest.fixture(scope='function') -def app_request(): - """Return a session-wide application configured in TEST mode.""" - _app = create_app('testing') - return _app - - -@pytest.fixture(scope='session') -def client(app): # pylint: disable=redefined-outer-name - """Return a session-wide Flask test client.""" - return app.test_client() - - -@pytest.fixture(scope='session') -def client_ctx(app): # pylint: disable=redefined-outer-name - """Return session-wide Flask test client.""" - with app.test_client() as _client: - yield _client - - -@pytest.fixture(scope='session') -def db(app): # pylint: disable=redefined-outer-name, invalid-name - """Return a session-wide initialised database. - - Drops all existing tables - Meta follows Postgres FKs - """ - with app.app_context(): - # Clear out any existing tables - metadata = MetaData(_db.engine) - metadata.reflect() - for table in metadata.tables.values(): - for fk in table.foreign_keys: # pylint: disable=invalid-name - _db.engine.execute(DropConstraint(fk.constraint)) - metadata.drop_all() - _db.drop_all() - - sequence_sql = """SELECT sequence_name FROM information_schema.sequences - WHERE sequence_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(sequence_sql))]: - try: - sess.execute(text(f'DROP SEQUENCE public.{seq} ;')) - print(f'DROP SEQUENCE public.{seq} ') - except Exception as err: # NOQA # pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # ############################################ - # There are 2 approaches, an empty database, or the same one that the app will use - # create the tables - # _db.create_all() - # or - # Use Alembic to load all of the DB revisions including supporting lookup data - # This is the path we'll use in legal_api!! - - # even though this isn't referenced directly, it sets up the internal configs that upgrade needs - migrations_path = [folder for folder in sys.path if 'pay-api/pay-api' in folder][0] \ - .replace('/pay-api/src', '/pay-api/migrations') - - Migrate(app, _db, directory=migrations_path) - upgrade() - - return _db + return create_app('testing') -@pytest.fixture(scope='function') +@pytest.fixture(scope='function', autouse=True) def session(app): # pylint: disable=redefined-outer-name, invalid-name """Return a function-scoped session.""" with app.app_context(): diff --git a/jobs/ftp-poller/tests/jobs/test_sftp.py b/jobs/ftp-poller/tests/jobs/test_sftp.py index af347ca8b..07dc79d5b 100644 --- a/jobs/ftp-poller/tests/jobs/test_sftp.py +++ b/jobs/ftp-poller/tests/jobs/test_sftp.py @@ -16,27 +16,22 @@ Test-Suite to ensure that the CreateAccountTask is working as expected. """ -# import os -# from typing import List - -from flask import current_app import pytest +from flask import current_app from sbc_common_components.utils.enums import QueueMessageTypes from services.sftp import SFTPService from utils.utils import publish_to_queue -# from utils.minio import get_object -# from tasks.poll_ftp_task import PollFtpTask -def test_cget_sftp_connection(session): # pylint:disable=unused-argument +def test_cget_sftp_connection(): """Test create account.""" con = SFTPService.get_connection() assert con -def test_poll_ftp_task(session): # pylint:disable=unused-argument +def test_poll_ftp_task(): """Test Poll.""" con = SFTPService.get_connection() @@ -44,14 +39,6 @@ def test_poll_ftp_task(session): # pylint:disable=unused-argument files = con.listdir(ftp_dir) assert len(files) == 1, 'Files exist in FTP folder' - # TODO fixed this test case - # payment_file_list: List[str] = PollFtpTask.poll_ftp() - # minio_file_content = get_object(payment_file_list[0]).read().decode() - # full_path = os.path.join(os.path.dirname(__file__), '../docker/ftp/test.txt') - # sftp_local_file_content = open(full_path, 'r').read() - # assert minio_file_content == sftp_local_file_content, 'minio upload works fine.' \ - # 'Contents of ftp drive and minio verified' - @pytest.mark.skip(reason='leave this to manually verify pubsub connection;' 'needs env vars, disable def mock_queue_publish(monkeypatch):') diff --git a/jobs/ftp-poller/utils/logger.py b/jobs/ftp-poller/utils/logger.py index 8b88ddcf2..afa83de96 100755 --- a/jobs/ftp-poller/utils/logger.py +++ b/jobs/ftp-poller/utils/logger.py @@ -18,14 +18,9 @@ def setup_logging(conf): - """Create the services logger. - - TODO should be reworked to load in the proper loggers and remove others - """ - # log_file_path = path.join(path.abspath(path.dirname(__file__)), conf) - + """Create the services logger.""" if conf and path.isfile(conf): logging.config.fileConfig(conf) - print(f'Configure logging, from conf:{conf}', file=sys.stdout) + print(f'Configure logging, from conf: {conf}', file=sys.stdout) else: - print(f'Unable to configure logging, attempted conf:{conf}', file=sys.stderr) + print(f'Unable to configure logging, attempted conf: {conf}', file=sys.stderr) diff --git a/jobs/notebook-report/Dockerfile b/jobs/notebook-report/Dockerfile index cab50539f..06a79a2b3 100644 --- a/jobs/notebook-report/Dockerfile +++ b/jobs/notebook-report/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye ARG VCS_REF="missing" ARG BUILD_DATE="missing" diff --git a/jobs/notebook-report/notebookreport.py b/jobs/notebook-report/notebookreport.py index 9af2644ed..c8fff20e4 100644 --- a/jobs/notebook-report/notebookreport.py +++ b/jobs/notebook-report/notebookreport.py @@ -50,6 +50,9 @@ def send_email(file_processing, emailtype, errormessage, partner_code=None): message = MIMEMultipart() date_str = datetime.strftime(datetime.now() - timedelta(1), '%Y-%m-%d') ext = '' + filenames = [] + subject = '' + recipients = '' if not Config.ENVIRONMENT == 'prod': ext = ' on ' + Config.ENVIRONMENT diff --git a/jobs/notebook-report/requirements.txt b/jobs/notebook-report/requirements.txt index 4a095802e..016fe67b3 100644 --- a/jobs/notebook-report/requirements.txt +++ b/jobs/notebook-report/requirements.txt @@ -1,6 +1,6 @@ jupyter SQLAlchemy==1.3.16 -psycopg2-binary==2.8.5 +psycopg2-binary==2.9.9 ipython-sql simplejson pandas diff --git a/jobs/payment-jobs/Dockerfile b/jobs/payment-jobs/Dockerfile index c76d801ed..981cae2db 100644 --- a/jobs/payment-jobs/Dockerfile +++ b/jobs/payment-jobs/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye # ======================================================================================================== # Install go-crond (from https://github.com/BCDevOps/go-crond) @@ -50,16 +50,60 @@ RUN mkdir $HOME && chmod 755 $HOME WORKDIR $HOME -# Install the requirements -COPY ./requirements.txt . -COPY ./requirements/ ./requirements/ -COPY . . +ARG APP_ENV \ + # Needed for fixing permissions of files created by Docker: + UID=1000 \ + GID=1000 + +ENV APP_ENV=${APP_ENV} \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_DEFAULT_TIMEOUT=100 \ + PIP_ROOT_USER_ACTION=ignore \ + # poetry: + POETRY_VERSION=1.8.3 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + POETRY_HOME='/usr/local' -RUN pip install --upgrade pip -RUN pip install --no-cache-dir -r requirements.txt +SHELL ["/bin/bash", "-eo", "pipefail", "-c"] +RUN apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y \ + bash \ + brotli \ + build-essential \ + curl \ + gettext \ + git \ + libpq-dev \ + wait-for-it \ + && curl -sSL 'https://install.python-poetry.org' | python - \ + && poetry --version \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* +# Copy only requirements, to cache them in docker layer +COPY --chown=web:web ./poetry.lock ./pyproject.toml . +# Project initialization: +RUN --mount=type=cache,target="$POETRY_CACHE_DIR" \ + echo "$APP_ENV" \ + && poetry version \ + && poetry run pip install -U pip \ + && poetry install \ + $(if [ -z ${APP_ENV+x} ] | [ "$APP_ENV" = 'production' ]; then echo '--only main'; fi) \ + --no-interaction --no-ansi + +COPY . . # Set ownership and permissions # Set scripts as executable (make files and python files do not have to be marked) # Make /etc/passwd writable for the root group so an entry can be created for an OpenShift assigned user account. diff --git a/jobs/payment-jobs/Makefile b/jobs/payment-jobs/Makefile index 898950b96..942f7e719 100644 --- a/jobs/payment-jobs/Makefile +++ b/jobs/payment-jobs/Makefile @@ -12,7 +12,7 @@ DOCKER_NAME:=payment-job ################################################################################# # COMMANDS -- Setup # ################################################################################# -setup: install install-dev ## Setup the project +setup: install ## Setup the project clean: clean-build clean-pyc clean-test ## Clean the project rm -rf venv/ @@ -36,24 +36,9 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -build-req: clean ## Upgrade requirements - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements/prod.txt ;\ - pip install -Ur requirements/bcregistry-libraries.txt ;\ - pip freeze | sort > requirements.txt ;\ - -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements.txt - -install-dev: ## Install local application - . venv/bin/activate ; \ - pip install -Ur requirements/dev.txt; \ - pip install -e . +install: clean + pip install poetry ;\ + poetry install ################################################################################# # COMMANDS - CI # @@ -61,15 +46,15 @@ install-dev: ## Install local application ci: lint flake8 test ## CI flow pylint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg tasks + poetry run pylint --rcfile=setup.cfg tasks flake8: ## Linting with flake8 - . venv/bin/activate && flake8 tasks tests + poetry run flake8 tasks tests lint: pylint flake8 ## run all lint type scripts test: ## Unit testing - . venv/bin/activate && pytest + poetry run pytest mac-cov: test ## Run the coverage report and display in a browser window (mac) @open -a "Google Chrome" htmlcov/index.html diff --git a/jobs/payment-jobs/config.py b/jobs/payment-jobs/config.py index 524641e2a..a18c89791 100644 --- a/jobs/payment-jobs/config.py +++ b/jobs/payment-jobs/config.py @@ -64,14 +64,12 @@ class _Config(object): # pylint: disable=too-few-public-methods DB_NAME = os.getenv('DATABASE_NAME', '') DB_HOST = os.getenv('DATABASE_HOST', '') DB_PORT = os.getenv('DATABASE_PORT', '5432') - - SQLALCHEMY_DATABASE_URI = 'postgresql://{user}:{password}@{host}:{port}/{name}'.format( - user=DB_USER, - password=DB_PASSWORD, - host=DB_HOST, - port=int(DB_PORT), - name=DB_NAME, - ) + if DB_UNIX_SOCKET := os.getenv('DATABASE_UNIX_SOCKET', None): + SQLALCHEMY_DATABASE_URI = ( + f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432' + ) + else: + SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' SQLALCHEMY_ECHO = False ORACLE_USER = os.getenv('ORACLE_USER', '') @@ -97,8 +95,14 @@ class _Config(object): # pylint: disable=too-few-public-methods # legislative timezone for future effective dating LEGISLATIVE_TIMEZONE = os.getenv('LEGISLATIVE_TIMEZONE', 'America/Vancouver') - # notify-API URL - NOTIFY_API_URL = os.getenv('NOTIFY_API_URL') + # API Endpoints + AUTH_API_URL = os.getenv('AUTH_API_URL', '') + AUTH_API_VERSION = os.getenv('AUTH_API_VERSION', '') + NOTIFY_API_URL = os.getenv('NOTIFY_API_URL', '') + NOTIFY_API_VERSION = os.getenv('NOTIFY_API_VERSION', '') + + AUTH_API_ENDPOINT = f'{AUTH_API_URL + AUTH_API_VERSION}/' + NOTIFY_API_ENDPOINT = f'{NOTIFY_API_URL + NOTIFY_API_VERSION}/' # Service account details KEYCLOAK_SERVICE_ACCOUNT_ID = os.getenv('SBC_AUTH_ADMIN_CLIENT_ID') @@ -116,8 +120,6 @@ class _Config(object): # pylint: disable=too-few-public-methods ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', 'account-mailer-dev') GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) - # Auth API Endpoint - AUTH_API_ENDPOINT = f'{os.getenv("AUTH_API_URL")}/' CFS_ACCOUNT_DESCRIPTION = os.getenv('CFS_ACCOUNT_DESCRIPTION', 'BCR') CFS_INVOICE_PREFIX = os.getenv('CFS_INVOICE_PREFIX', 'REG') @@ -176,9 +178,6 @@ class _Config(object): # pylint: disable=too-few-public-methods # disbursement delay DISBURSEMENT_DELAY_IN_DAYS = int(os.getenv('DISBURSEMENT_DELAY', 5)) - # Is FAS-CFS integration disabled - DISABLE_CFS_FAS_INTEGRATION = os.getenv('DISABLE_CFS_FAS_INTEGRATION', 'false').lower() == 'true' - # CP Job variables CGI_AP_DISTRIBUTION = os.getenv('CGI_AP_DISTRIBUTION', '') CGI_AP_SUPPLIER_NUMBER = os.getenv('CGI_AP_SUPPLIER_NUMBER', '') @@ -186,7 +185,7 @@ class _Config(object): # pylint: disable=too-few-public-methods CGI_AP_REMITTANCE_CODE = os.getenv('CGI_AP_REMITTANCE_CODE', '78') BCA_SUPPLIER_NUMBER = os.getenv('BCA_SUPPLIER_NUMBER', '') BCA_SUPPLIER_LOCATION = os.getenv('BCA_SUPPLIER_LOCATION', '') - + # FAS Client and secret CFS_FAS_CLIENT_ID = os.getenv('CFS_FAS_CLIENT_ID', '') CFS_FAS_CLIENT_SECRET = os.getenv('CFS_FAS_CLIENT_SECRET', '') @@ -195,6 +194,11 @@ class _Config(object): # pylint: disable=too-few-public-methods EFT_HOLDING_GL = os.getenv('EFT_HOLDING_GL', '') EFT_TRANSFER_DESC = os.getenv('EFT_TRANSFER_DESC', 'BCREGISTRIES {} {} EFT TRANSFER') + # GCP PubSub + AUDIENCE = os.getenv('AUDIENCE', None) + GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) + PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) + ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) class DevConfig(_Config): # pylint: disable=too-few-public-methods TESTING = False @@ -212,7 +216,7 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods DB_HOST = os.getenv('DATABASE_TEST_HOST', '') DB_PORT = os.getenv('DATABASE_TEST_PORT', '5432') SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_TEST_URL', - 'postgresql://{user}:{password}@{host}:{port}/{name}'.format( + 'postgresql+pg8000://{user}:{password}@{host}:{port}/{name}'.format( user=DB_USER, password=DB_PASSWORD, host=DB_HOST, @@ -244,6 +248,7 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods CGI_SFTP_PORT = 2222 CGI_SFTP_DIRECTORY = '/data/' CGI_SFTP_HOST = 'localhost' + GCP_AUTH_KEY = None class ProdConfig(_Config): # pylint: disable=too-few-public-methods diff --git a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml new file mode 100644 index 000000000..96fde8372 --- /dev/null +++ b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml @@ -0,0 +1,94 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: dev +description: Dev Environment +deployParameters: + deploy-env: "development" + deploy-project-id: "gtksf3-dev" + job-name: "payment-jobs-dev" + app-env: "dev" + cloudsql-instances: "gtksf3-dev:northamerica-northeast1:pay-db-dev" + run-command: "./run.sh" + service-account: "sa-job@gtksf3-dev.iam.gserviceaccount.com" +run: + location: projects/gtksf3-dev/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: test +description: Test Environment +deployParameters: + deploy-env: "development" + deploy-project-id: "gtksf3-test" + job-name: "payment-jobs-test" + app-env: "test" + cloudsql-instances: "gtksf3-test:northamerica-northeast1:pay-db-test" + run-command: "./run.sh" + service-account: "sa-job@gtksf3-test.iam.gserviceaccount.com" +run: + location: projects/gtksf3-test/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: sandbox +description: Sandbox Environment +requireApproval: true +deployParameters: + deploy-env: "production" + deploy-project-id: "gtksf3-tools" + job-name: "payment-jobs-sandbox" + app-env: "sandbox" + cloudsql-instances: "gtksf3-tools:northamerica-northeast1:pay-db-sandbox" + run-command: "./run.sh" + service-account: "sa-job@gtksf3-tools.iam.gserviceaccount.com" +run: + location: projects/gtksf3-tools/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: prod +description: Production Environment +requireApproval: true +deployParameters: + deploy-env: "production" + deploy-project-id: "gtksf3-prod" + job-name: "payment-jobs-prod" + app-env: "production" + cloudsql-instances: "gtksf3-prod:northamerica-northeast1:pay-db-prod" + run-command: "./run.sh" + service-account: "sa-job@gtksf3-prod.iam.gserviceaccount.com" +run: + location: projects/gtksf3-prod/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' \ No newline at end of file diff --git a/jobs/payment-jobs/devops/vaults.gcp.env b/jobs/payment-jobs/devops/vaults.gcp.env new file mode 100644 index 000000000..81e0907b7 --- /dev/null +++ b/jobs/payment-jobs/devops/vaults.gcp.env @@ -0,0 +1,88 @@ +PAY_LD_SDK_KEY="op://launchdarkly/$APP_ENV/pay/PAY_LD_SDK_KEY" +DATABASE_NAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_NAME" +DATABASE_PASSWORD="op://database/$APP_ENV/pay-db-gcp/DATABASE_PASSWORD" +DATABASE_PORT="op://database/$APP_ENV/pay-db-gcp/DATABASE_PORT" +DATABASE_UNIX_SOCKET="op://database/$APP_ENV/pay-db-gcp/DATABASE_UNIX_SOCKET" +DATABASE_USERNAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_USERNAME" +JWT_OIDC_ISSUER="op://keycloak/$APP_ENV/jwt-base/JWT_OIDC_ISSUER" +SBC_AUTH_ADMIN_CLIENT_ID="op://keycloak/$APP_ENV/sbc-auth-admin/SBC_AUTH_ADMIN_CLIENT_ID" +SBC_AUTH_ADMIN_CLIENT_SECRET="op://keycloak/$APP_ENV/sbc-auth-admin/SBC_AUTH_ADMIN_CLIENT_SECRET" +CFS_BASE_URL="op://payment-external-services/$APP_ENV/cfs/CFS_BASE_URL" +CFS_CLIENT_ID="op://payment-external-services/$APP_ENV/cfs/CFS_CLIENT_ID" +CFS_CLIENT_SECRET="op://payment-external-services/$APP_ENV/cfs/CFS_CLIENT_SECRET" +CONNECT_TIMEOUT="op://payment-external-services/$APP_ENV/cfs/CONNECT_TIMEOUT" +CFS_GENERATE_RANDOM_INVOICE_NUMBER="op://payment-external-services/$APP_ENV/cfs/CFS_GENERATE_RANDOM_INVOICE_NUMBER" +CFS_ACCOUNT_DESCRIPTION="op://payment-external-services/$APP_ENV/cfs/CFS_ACCOUNT_DESCRIPTION" +CFS_INVOICE_PREFIX="op://payment-external-services/$APP_ENV/cfs/CFS_INVOICE_PREFIX" +CFS_PARTY_PREFIX="op://payment-external-services/$APP_ENV/cfs/CFS_PARTY_PREFIX" +CFS_STOP_PAD_ACCOUNT_CREATION="op://payment-external-services/$APP_ENV/cfs/CFS_STOP_PAD_ACCOUNT_CREATION" +CFS_INVOICE_CUT_OFF_HOURS_UTC="op://payment-external-services/$APP_ENV/cfs/CFS_INVOICE_CUT_OFF_HOURS_UTC" +CFS_INVOICE_CUT_OFF_MINUTES_UTC="op://payment-external-services/$APP_ENV/cfs/CFS_INVOICE_CUT_OFF_MINUTES_UTC" +CFS_FAS_CLIENT_ID="op://payment-external-services/$APP_ENV/cfs/CFS_FAS_CLIENT_ID" +CFS_FAS_CLIENT_SECRET="op://payment-external-services/$APP_ENV/cfs/CFS_FAS_CLIENT_SECRET" +EFT_INVOICE_PREFIX="op://payment-external-services/$APP_ENV/eft/EFT_INVOICE_PREFIX" +EFT_HOLDING_GL="" +EFT_TRANSFER_DESC="op://payment-external-services/$APP_ENV/eft/EFT_TRANSFER_DESC" +PAYBC_DIRECT_PAY_REF_NUMBER="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_REF_NUMBER" +PAYBC_DIRECT_PAY_API_KEY="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_API_KEY" +PAYBC_DIRECT_PAY_BASE_URL="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_BASE_URL" +PAYBC_DIRECT_PAY_CLIENT_ID="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_CLIENT_ID" +PAYBC_DIRECT_PAY_CLIENT_SECRET="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_CLIENT_SECRET" +AUDIENCE="op://gcp-queue/$APP_ENV/base/AUDIENCE" +GCP_AUTH_KEY="op://gcp-queue/$APP_ENV/base/GCP_AUTH_KEY" +PUBLISHER_AUDIENCE="op://gcp-queue/$APP_ENV/base/PUBLISHER_AUDIENCE" +ACCOUNT_MAILER_TOPIC="op://gcp-queue/$APP_ENV/topics/ACCOUNT_MAILER_TOPIC" +AUTH_API_URL="op://API/$APP_ENV/auth-api/AUTH_API_URL" +AUTH_API_VERSION="op://API/$APP_ENV/auth-api/AUTH_API_VERSION" +NOTIFY_API_URL="op://API/$APP_ENV/notify-api/NOTIFY_API_URL" +NOTIFY_API_VERSION="op://API/$APP_ENV/notify-api/NOTIFY_API_VERSION" +SENTRY_ENABLE="op://sentry/$APP_ENV/relationship-api/SENTRY_ENABLE" +SENTRY_DSN="op://sentry/$APP_ENV/relationship-api/SENTRY_DSN" +PAD_CONFIRMATION_PERIOD_IN_DAYS="op://relationship/$APP_ENV/pay-api/PAD_CONFIRMATION_PERIOD_IN_DAYS" +DISABLE_VALID_REDIRECT_URLS="op://relationship/$APP_ENV/pay-api/DISABLE_VALID_REDIRECT_URLS" +VALID_REDIRECT_URLS="op://relationship/$APP_ENV/pay-api/VALID_REDIRECT_URLS" +TRANSACTION_REPORT_DEFAULT_TOTAL="op://relationship/$APP_ENV/pay-api/TRANSACTION_REPORT_DEFAULT_TOTAL" +ROUTING_SLIP_DEFAULT_TOTAL="op://relationship/$APP_ENV/pay-api/ROUTING_SLIP_DEFAULT_TOTAL" +LEGISLATIVE_TIMEZONE="op://relationship/$APP_ENV/pay-api/LEGISLATIVE_TIMEZONE" +BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS="op://relationship/$APP_ENV/pay-api/BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS" +MASK_LEN="op://relationship/$APP_ENV/pay-api/MASK_LEN" +DISABLE_ACTIVITY_LOGS="op://relationship/$APP_ENV/pay-api/DISABLE_ACTIVITY_LOGS" +ACCOUNT_SECRET_KEY="op://relationship/$APP_ENV/pay-api/ACCOUNT_SECRET_KEY" +MINIO_ENDPOINT="op://minio/$APP_ENV/base/MINIO_ENDPOINT" +MINIO_ACCESS_KEY="op://minio/$APP_ENV/base/MINIO_ACCESS_KEY" +MINIO_ACCESS_SECRET="op://minio/$APP_ENV/base/MINIO_ACCESS_SECRET" +MINIO_SECURE="op://minio/$APP_ENV/payment-jobs/MINIO_SECURE" +MINIO_EJV_BUCKET_NAME="op://minio/$APP_ENV/payment-jobs/MINIO_EJV_BUCKET_NAME" +CGI_FEEDER_NUMBER="op://relationship/$APP_ENV/payment-jobs/CGI_FEEDER_NUMBER" +CGI_MINISTRY_PREFIX="op://relationship/$APP_ENV/payment-jobs/CGI_MINISTRY_PREFIX" +CGI_DISBURSEMENT_DESC="op://relationship/$APP_ENV/payment-jobs/CGI_DISBURSEMENT_DESC" +CGI_MESSAGE_VERSION="op://relationship/$APP_ENV/payment-jobs/CGI_MESSAGE_VERSION" +CGI_BCREG_CLIENT_CODE="op://relationship/$APP_ENV/payment-jobs/CGI_BCREG_CLIENT_CODE" +CGI_EJV_SUPPLIER_NUMBER="op://relationship/$APP_ENV/payment-jobs/CGI_EJV_SUPPLIER_NUMBER" +CGI_TRIGGER_FILE_SUFFIX="op://relationship/$APP_ENV/payment-jobs/CGI_TRIGGER_FILE_SUFFIX" +NOTIFY_AFTER_DAYS="op://relationship/$APP_ENV/payment-jobs/NOTIFY_AFTER_DAYS" +CGI_AP_DISTRIBUTION="op://relationship/$APP_ENV/payment-jobs/CGI_AP_DISTRIBUTION" +CGI_AP_SUPPLIER_NUMBER="op://relationship/$APP_ENV/payment-jobs/CGI_AP_SUPPLIER_NUMBER" +CGI_AP_SUPPLIER_LOCATION="op://relationship/$APP_ENV/payment-jobs/CGI_AP_SUPPLIER_LOCATION" +CGI_AP_REMITTANCE_CODE="op://relationship/$APP_ENV/payment-jobs/CGI_AP_REMITTANCE_CODE" +BCA_SUPPLIER_NUMBER="op://relationship/$APP_ENV/payment-jobs/BCA_SUPPLIER_NUMBER" +BCA_SUPPLIER_LOCATION="op://relationship/$APP_ENV/payment-jobs/BCA_SUPPLIER_LOCATION" +CGI_SFTP_HOST="op://relationship/$APP_ENV/ftp-poller/CAS_SFTP_HOST" +CGI_SFTP_USERNAME="op://relationship/$APP_ENV/ftp-poller/CAS_SFTP_USER_NAME" +CGI_SFTP_PASSWORD="" +CGI_SFTP_VERIFY_HOST="op://relationship/$APP_ENV/ftp-poller/SFTP_VERIFY_HOST" +CGI_SFTP_HOST_KEY="op://relationship/$APP_ENV/ftp-poller/CAS_SFTP_HOST_KEY" +CGI_SFTP_PORT="21" +BCREG_CGI_FTP_PRIVATE_KEY="op://relationship/$APP_ENV/ftp-poller/BCREG_FTP_PRIVATE_KEY" +BCREG_CGI_FTP_PRIVATE_KEY_PASSPHRASE="op://relationship/$APP_ENV/ftp-poller/BCREG_FTP_PRIVATE_KEY_PASSPHRASE" +CGI_SFTP_DIRECTORY="op://relationship/$APP_ENV/ftp-poller/CAS_SFTP_DIRECTORY" +AUTH_WEB_PAY_TRANSACTION_URL="op://web-url/$APP_ENV/auth-web/AUTH_WEB_URL" +AUTH_WEB_STATEMENT_URL="op://web-url/$APP_ENV/auth-web/AUTH_WEB_URL" +REGISTRIES_LOGO_IMAGE_NAME="bc_logo_for_email.png" +DISBURSEMENT_DELAY="5" +DISABLE_CFS_FAS_INTEGRATION="false" +ORACLE_USER="" +ORACLE_PASSWORD="" +ORACLE_DB_NAME="" +ORACLE_HOST="" +ORACLE_PORT="1521" \ No newline at end of file diff --git a/jobs/payment-jobs/invoke_jobs.py b/jobs/payment-jobs/invoke_jobs.py index 9e6371f27..7636770a6 100755 --- a/jobs/payment-jobs/invoke_jobs.py +++ b/jobs/payment-jobs/invoke_jobs.py @@ -24,8 +24,8 @@ import config from services import oracle_db -from tasks.eft_transfer_task import EftTransferTask from tasks.routing_slip_task import RoutingSlipTask +from tasks.electronic_funds_transfer_task import ElectronicFundsTransferTask from tasks.statement_due_task import StatementDueTask from utils.logger import setup_logging @@ -35,11 +35,12 @@ setup_logging(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'logging.conf')) # important to do this first -def create_app(run_mode=os.getenv('FLASK_ENV', 'production'), job_name='unknown', init_oracle=False): +def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production'), job_name='unknown', init_oracle=False): """Return a configured Flask App using the Factory method.""" from pay_api.models import db, ma app = Flask(__name__) + app.env = run_mode app.config.from_object(config.CONFIGURATION[run_mode]) # Configure Sentry @@ -50,7 +51,7 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production'), job_name='unknown' integrations=[FlaskIntegration()], release=f'payment-jobs-{job_name}@-', ) - app.logger.info(f'<<<< Starting Payment Jobs >>>>') + app.logger.info('<<<< Starting Payment Jobs >>>>') queue.init_app(app) db.init_app(app) if init_oracle: @@ -132,6 +133,10 @@ def run(job_name, argument=None): RoutingSlipTask.process_correction() RoutingSlipTask.adjust_routing_slips() application.logger.info(f'<<<< Completed Routing Slip tasks >>>>') + elif job_name == 'EFT': + ElectronicFundsTransferTask.link_electronic_funds_transfers() + ElectronicFundsTransferTask.unlink_electronic_funds_transfers() + application.logger.info(f'<<<< Completed EFT tasks >>>>') elif job_name == 'EJV_PAYMENT': EjvPaymentTask.create_ejv_file() application.logger.info(f'<<<< Completed running EJV payment >>>>') @@ -144,9 +149,6 @@ def run(job_name, argument=None): elif job_name == 'BCOL_REFUND_CONFIRMATION': BcolRefundConfirmationTask.update_bcol_refund_invoices() application.logger.info(f'<<<< Completed running BCOL Refund Confirmation Job >>>>') - elif job_name == 'EFT_TRANSFER': - EftTransferTask.create_ejv_file() - application.logger.info(f'<<<< Completed Creating EFT Transfer File for transfer to internal GLs>>>>') else: application.logger.debug('No valid args passed. Exiting job without running any ***************') diff --git a/jobs/payment-jobs/openshift/payment-job-build.json b/jobs/payment-jobs/openshift/payment-job-build.json deleted file mode 100644 index 2960fadd2..000000000 --- a/jobs/payment-jobs/openshift/payment-job-build.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Build template for a payments job.", - "tags": "flask", - "iconClass": "icon-python" - }, - "name": "${NAME}-build-template" - }, - "objects": [ - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}" - } - }, - { - "kind": "BuildConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}", - "labels": { - "app": "${NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-build" - } - }, - "spec": { - "source": { - "type": "Git", - "git": { - "uri": "${GIT_REPO_URL}", - "ref": "${GIT_REF}" - }, - "contextDir": "${SOURCE_CONTEXT_DIR}" - }, - "strategy": { - "type": "Docker", - "dockerStrategy": { - "dockerfilePath": "${DOCKER_FILE_PATH}" - } - }, - "output": { - "to": { - "kind": "ImageStreamTag", - "name": "${NAME}:${OUTPUT_IMAGE_TAG}" - } - }, - "triggers": [ - { - "type": "ConfigChange" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the objects defined in this template. You should keep this as default unless your know what your doing.", - "required": true, - "value": "payment-job" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "GIT_REPO_URL", - "displayName": "Git Repo URL", - "description": "The URL to your GIT repo, don't use the this default unless your just experimenting.", - "required": true, - "value": "https://github.com/bcgov/sbc-pay.git" - }, - { - "name": "GIT_REF", - "displayName": "Git Reference", - "description": "The git reference or branch.", - "required": true, - "value": "development" - }, - { - "name": "SOURCE_CONTEXT_DIR", - "displayName": "Source Context Directory", - "description": "The source context directory.", - "required": true, - "value": "jobs/payment-jobs" - }, - { - "name": "SOURCE_IMAGE_KIND", - "displayName": "Source Image Kind", - "required": true, - "description": "The 'kind' (type) of the source image; typically ImageStreamTag, or DockerImage.", - "value": "ImageStreamTag" - }, - { - "name": "SOURCE_IMAGE_NAME_SPACE", - "displayName": "Source Image Name Space", - "required": true, - "description": "The name space of the source image.", - "value": "d7eovc-tools" - }, - { - "name": "SOURCE_IMAGE_NAME", - "displayName": "Source Image Name", - "required": true, - "description": "The name of the source image.", - "value": "python" - }, - { - "name": "SOURCE_IMAGE_TAG", - "displayName": "Source Image Tag", - "required": true, - "description": "The tag of the source image.", - "value": "3.7" - }, - { - "name": "OUTPUT_IMAGE_TAG", - "displayName": "Output Image Tag", - "description": "The tag given to the built image.", - "required": true, - "value": "latest" - }, - { - "name": "DOCKER_FILE_PATH", - "displayName": "Docker File Path", - "description": "The path to the docker file defining the build.", - "required": false, - "value": "Dockerfile" - } - ] -} diff --git a/jobs/payment-jobs/openshift/payment-job-deploy.json b/jobs/payment-jobs/openshift/payment-job-deploy.json deleted file mode 100644 index 58d062e75..000000000 --- a/jobs/payment-jobs/openshift/payment-job-deploy.json +++ /dev/null @@ -1,417 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for a payment job.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%" - } - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "${IMAGE_NAMESPACE}", - "name": "${NAME}:${TAG_NAME}" - } - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": 1, - "test": false, - "selector": { - "app": "${NAME}-${TAG_NAME}", - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "template": { - "metadata": { - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "deploymentconfig": "${NAME}-${TAG_NAME}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "volumes": [ - { - "name": "cron-config", - "configMap": { - "name": "${NAME}-${TAG_NAME}-cron-configuration", - "defaultMode": 420 - } - }, - { - "name": "sftp-private-key", - "configMap": { - "name": "ftp-poller-${TAG_NAME}-sftp-configuration", - "defaultMode": 420 - } - } - ], - "containers": [ - { - "name": "${NAME}-${TAG_NAME}", - "image": "docker-registry.default.svc:5000/${IMAGE_NAMESPACE}/${NAME}:${TAG_NAME}", - "ports": [ - { - "containerPort": 8080, - "protocol": "TCP" - } - ], - "volumeMounts": [ - { - "name": "cron-config", - "readOnly": true, - "mountPath": "/payment-jobs/cron/" - }, - { - "name": "sftp-private-key", - "readOnly": true, - "mountPath": "/payment-jobs/key/" - } - ], - "env": [ - { - "name": "DATABASE_USERNAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_USER" - } - } - }, - { - "name": "DATABASE_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-secret", - "key": "DATABASE_PASSWORD" - } - } - }, - { - "name": "DATABASE_NAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_NAME" - } - } - }, - { - "name": "DATABASE_HOST", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_HOST" - } - } - }, - { - "name": "DATABASE_PORT", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_PORT" - } - } - }, - { - "name": "DATABASE_TEST_USERNAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_USER" - } - } - }, - { - "name": "DATABASE_TEST_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-secret", - "key": "DATABASE_TEST_PASSWORD" - } - } - }, - { - "name": "DATABASE_TEST_NAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_NAME" - } - } - }, - { - "name": "DATABASE_TEST_HOST", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_HOST" - } - } - }, - { - "name": "DATABASE_TEST_PORT", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_PORT" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_REF_NUMBER", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_REF_NUMBER" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_API_KEY", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_API_KEY" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_BASE_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_BASE_URL" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_CLIENT_ID", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_CLIENT_ID" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_CLIENT_SECRET", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_CLIENT_SECRET" - } - } - }, - { - "name": "NOTIFY_API_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "NOTIFY_API_URL" - } - } - }, - { - "name": "KEYCLOAK_SERVICE_ACCOUNT_ID", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "KEYCLOAK_SERVICE_ACCOUNT_ID" - } - } - }, - { - "name": "KEYCLOAK_SERVICE_ACCOUNT_SECRET", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "KEYCLOAK_SERVICE_ACCOUNT_SECRET" - } - } - }, - { - "name": "JWT_OIDC_ISSUER", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "JWT_OIDC_ISSUER" - } - } - }, - { - "name": "AUTH_WEB_PAY_TRANSACTION_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "AUTH_WEB_PAY_TRANSACTION_URL" - } - } - } - ], - "resources": { - "requests": { - "cpu": "${CPU_REQUEST}", - "memory": "${MEMORY_REQUEST}" - }, - "limits": { - "cpu": "${CPU_LIMIT}", - "memory": "${MEMORY_LIMIT}" - } - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - } - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "creationTimestamp": null, - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "ports": [ - { - "name": "8080-tcp", - "protocol": "TCP", - "port": 8080, - "targetPort": 8080 - } - ], - "selector": { - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "payment-job" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - } - ] -} \ No newline at end of file diff --git a/jobs/payment-jobs/poetry.lock b/jobs/payment-jobs/poetry.lock new file mode 100644 index 000000000..0a123832e --- /dev/null +++ b/jobs/payment-jobs/poetry.lock @@ -0,0 +1,2747 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + +[[package]] +name = "argon2-cffi" +version = "23.1.0" +description = "Argon2 for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, + {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, +] + +[package.dependencies] +argon2-cffi-bindings = "*" + +[package.extras] +dev = ["argon2-cffi[tests,typing]", "tox (>4)"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"] +tests = ["hypothesis", "pytest"] +typing = ["mypy"] + +[[package]] +name = "argon2-cffi-bindings" +version = "21.2.0" +description = "Low-level CFFI bindings for Argon2" +optional = false +python-versions = ">=3.6" +files = [ + {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, + {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, +] + +[package.dependencies] +cffi = ">=1.0.1" + +[package.extras] +dev = ["cogapp", "pre-commit", "pytest", "wheel"] +tests = ["pytest"] + +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "autopep8" +version = "2.1.0" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.8" +files = [ + {file = "autopep8-2.1.0-py2.py3-none-any.whl", hash = "sha256:2bb76888c5edbcafe6aabab3c47ba534f5a2c2d245c2eddced4a30c4b4946357"}, + {file = "autopep8-2.1.0.tar.gz", hash = "sha256:1fa8964e4618929488f4ec36795c7ff12924a68b8bf01366c094fc52f770b6e7"}, +] + +[package.dependencies] +pycodestyle = ">=2.11.0" + +[[package]] +name = "bcrypt" +version = "4.1.2" +description = "Modern password hashing for your software and your servers" +optional = false +python-versions = ">=3.7" +files = [ + {file = "bcrypt-4.1.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:ac621c093edb28200728a9cca214d7e838529e557027ef0581685909acd28b5e"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea505c97a5c465ab8c3ba75c0805a102ce526695cd6818c6de3b1a38f6f60da1"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57fa9442758da926ed33a91644649d3e340a71e2d0a5a8de064fb621fd5a3326"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb3bd3321517916696233b5e0c67fd7d6281f0ef48e66812db35fc963a422a1c"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6cad43d8c63f34b26aef462b6f5e44fdcf9860b723d2453b5d391258c4c8e966"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:44290ccc827d3a24604f2c8bcd00d0da349e336e6503656cb8192133e27335e2"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:732b3920a08eacf12f93e6b04ea276c489f1c8fb49344f564cca2adb663b3e4c"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c28973decf4e0e69cee78c68e30a523be441972c826703bb93099868a8ff5b5"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b8df79979c5bae07f1db22dcc49cc5bccf08a0380ca5c6f391cbb5790355c0b0"}, + {file = "bcrypt-4.1.2-cp37-abi3-win32.whl", hash = "sha256:fbe188b878313d01b7718390f31528be4010fed1faa798c5a1d0469c9c48c369"}, + {file = "bcrypt-4.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:9800ae5bd5077b13725e2e3934aa3c9c37e49d3ea3d06318010aa40f54c63551"}, + {file = "bcrypt-4.1.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:71b8be82bc46cedd61a9f4ccb6c1a493211d031415a34adde3669ee1b0afbb63"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e3c6642077b0c8092580c819c1684161262b2e30c4f45deb000c38947bf483"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:387e7e1af9a4dd636b9505a465032f2f5cb8e61ba1120e79a0e1cd0b512f3dfc"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f70d9c61f9c4ca7d57f3bfe88a5ccf62546ffbadf3681bb1e268d9d2e41c91a7"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2a298db2a8ab20056120b45e86c00a0a5eb50ec4075b6142db35f593b97cb3fb"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ba55e40de38a24e2d78d34c2d36d6e864f93e0d79d0b6ce915e4335aa81d01b1"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3566a88234e8de2ccae31968127b0ecccbb4cddb629da744165db72b58d88ca4"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b90e216dc36864ae7132cb151ffe95155a37a14e0de3a8f64b49655dd959ff9c"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:69057b9fc5093ea1ab00dd24ede891f3e5e65bee040395fb1e66ee196f9c9b4a"}, + {file = "bcrypt-4.1.2-cp39-abi3-win32.whl", hash = "sha256:02d9ef8915f72dd6daaef40e0baeef8a017ce624369f09754baf32bb32dba25f"}, + {file = "bcrypt-4.1.2-cp39-abi3-win_amd64.whl", hash = "sha256:be3ab1071662f6065899fe08428e45c16aa36e28bc42921c4901a191fda6ee42"}, + {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d75fc8cd0ba23f97bae88a6ec04e9e5351ff3c6ad06f38fe32ba50cbd0d11946"}, + {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a97e07e83e3262599434816f631cc4c7ca2aa8e9c072c1b1a7fec2ae809a1d2d"}, + {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e51c42750b7585cee7892c2614be0d14107fad9581d1738d954a262556dd1aab"}, + {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba4e4cc26610581a6329b3937e02d319f5ad4b85b074846bf4fef8a8cf51e7bb"}, + {file = "bcrypt-4.1.2.tar.gz", hash = "sha256:33313a1200a3ae90b75587ceac502b048b840fc69e7f7a0905b5f87fac7a1258"}, +] + +[package.extras] +tests = ["pytest (>=3.2.1,!=3.3.0)"] +typecheck = ["mypy"] + +[[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 = "cachelib" +version = "0.9.0" +description = "A collection of cache libraries in the same API interface." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachelib-0.9.0-py3-none-any.whl", hash = "sha256:811ceeb1209d2fe51cd2b62810bd1eccf70feba5c52641532498be5c675493b3"}, + {file = "cachelib-0.9.0.tar.gz", hash = "sha256:38222cc7c1b79a23606de5c2607f4925779e37cdcea1c2ad21b8bae94b5425a5"}, +] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "cattrs" +version = "23.2.3" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"}, + {file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"}, +] + +[package.dependencies] +attrs = ">=23.1.0" + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +orjson = ["orjson (>=3.9.2)"] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.4.4" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, + {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, + {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, + {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, + {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, + {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, + {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, + {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, + {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, + {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, + {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, + {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, + {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "croniter" +version = "2.0.2" +description = "croniter provides iteration for datetime object with cron like format" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "croniter-2.0.2-py2.py3-none-any.whl", hash = "sha256:78bf110a2c7dbbfdd98b926318ae6c64a731a4c637c7befe3685755110834746"}, + {file = "croniter-2.0.2.tar.gz", hash = "sha256:8bff16c9af4ef1fb6f05416973b8f7cb54997c02f2f8365251f9bf1dded91866"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = ">2021.1" + +[[package]] +name = "cryptography" +version = "42.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "cx-oracle" +version = "8.3.0" +description = "Python interface to Oracle" +optional = false +python-versions = "*" +files = [ + {file = "cx_Oracle-8.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b6a23da225f03f50a81980c61dbd6a358c3575f212ca7f4c22bb65a9faf94f7f"}, + {file = "cx_Oracle-8.3.0-cp310-cp310-win32.whl", hash = "sha256:715a8bbda5982af484ded14d184304cc552c1096c82471dd2948298470e88a04"}, + {file = "cx_Oracle-8.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:07f01608dfb6603a8f2a868fc7c7bdc951480f187df8dbc50f4d48c884874e6a"}, + {file = "cx_Oracle-8.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4b3afe7a911cebaceda908228d36839f6441cbd38e5df491ec25960562bb01a0"}, + {file = "cx_Oracle-8.3.0-cp36-cp36m-win32.whl", hash = "sha256:076ffb71279d6b2dcbf7df028f62a01e18ce5bb73d8b01eab582bf14a62f4a61"}, + {file = "cx_Oracle-8.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:b82e4b165ffd807a2bd256259a6b81b0a2452883d39f987509e2292d494ea163"}, + {file = "cx_Oracle-8.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b902db61dcdcbbf8dd981f5a46d72fef40c5150c7fc0eb0f0698b462d6eb834e"}, + {file = "cx_Oracle-8.3.0-cp37-cp37m-win32.whl", hash = "sha256:4c82ca74442c298ceec56d207450c192e06ecf8ad52eb4aaad0812e147ceabf7"}, + {file = "cx_Oracle-8.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:54164974d526b76fdefb0b66a42b68e1fca5df78713d0eeb8c1d0047b83f6bcf"}, + {file = "cx_Oracle-8.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:410747d542e5f94727f5f0e42e9706c772cf9094fb348ce965ab88b3a9e4d2d8"}, + {file = "cx_Oracle-8.3.0-cp38-cp38-win32.whl", hash = "sha256:3baa878597c5fadb2c72f359f548431c7be001e722ce4a4ebdf3d2293a1bb70b"}, + {file = "cx_Oracle-8.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:de42bdc882abdc5cea54597da27a05593b44143728e5b629ad5d35decb1a2036"}, + {file = "cx_Oracle-8.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:df412238a9948340591beee9ec64fa62a2efacc0d91107034a7023e2991fba97"}, + {file = "cx_Oracle-8.3.0-cp39-cp39-win32.whl", hash = "sha256:70d3cf030aefd71f99b45beba77237b2af448adf5e26be0db3d0d3dee6ea4230"}, + {file = "cx_Oracle-8.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:bf01ce87edb4ef663b2e5bd604e1e0154d2cc2f12b60301f788b569d9db8a900"}, + {file = "cx_Oracle-8.3.0.tar.gz", hash = "sha256:3b2d215af4441463c97ea469b9cc307460739f89fdfa8ea222ea3518f1a424d9"}, +] + +[[package]] +name = "dataclass-wizard" +version = "0.22.3" +description = "Marshal dataclasses to/from JSON. Use field properties with initial values. Construct a dataclass schema with JSON input." +optional = false +python-versions = "*" +files = [ + {file = "dataclass-wizard-0.22.3.tar.gz", hash = "sha256:4c46591782265058f1148cfd1f54a3a91221e63986fdd04c9d59f4ced61f4424"}, + {file = "dataclass_wizard-0.22.3-py2.py3-none-any.whl", hash = "sha256:63751203e54b9b9349212cc185331da73c1adc99c51312575eb73bb5c00c1962"}, +] + +[package.extras] +dev = ["Sphinx (==5.3.0)", "bump2version (==1.0.1)", "coverage (>=6.2)", "dataclass-factory (==2.12)", "dataclasses-json (==0.5.6)", "flake8 (>=3)", "jsons (==1.6.1)", "pip (>=21.3.1)", "pytest (==7.0.1)", "pytest-cov (==3.0.0)", "pytest-mock (>=3.6.1)", "pytimeparse (==1.1.8)", "sphinx-issues (==3.0.1)", "sphinx-issues (==4.0.0)", "tox (==3.24.5)", "twine (==3.8.0)", "watchdog[watchmedo] (==2.1.6)", "wheel (==0.37.1)", "wheel (==0.42.0)"] +timedelta = ["pytimeparse (>=1.1.7)"] +yaml = ["PyYAML (>=5.3)"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "dpath" +version = "2.1.6" +description = "Filesystem-like pathing and searching for dictionaries" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dpath-2.1.6-py3-none-any.whl", hash = "sha256:31407395b177ab63ef72e2f6ae268c15e938f2990a8ecf6510f5686c02b6db73"}, + {file = "dpath-2.1.6.tar.gz", hash = "sha256:f1e07c72e8605c6a9e80b64bc8f42714de08a789c7de417e49c3f87a19692e47"}, +] + +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "expiringdict" +version = "1.2.2" +description = "Dictionary with auto-expiring values for caching purposes" +optional = false +python-versions = "*" +files = [ + {file = "expiringdict-1.2.2-py3-none-any.whl", hash = "sha256:09a5d20bc361163e6432a874edd3179676e935eb81b925eccef48d409a8a45e8"}, + {file = "expiringdict-1.2.2.tar.gz", hash = "sha256:300fb92a7e98f15b05cf9a856c1415b3bc4f2e132be07daa326da6414c23ee09"}, +] + +[package.extras] +tests = ["coverage", "coveralls", "dill", "mock", "nose"] + +[[package]] +name = "faker" +version = "24.8.0" +description = "Faker is a Python package that generates fake data for you." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Faker-24.8.0-py3-none-any.whl", hash = "sha256:2f70a7817b4147d67c544192e169c5653060fce8aef758db0ea8823d89caac94"}, + {file = "Faker-24.8.0.tar.gz", hash = "sha256:1a46466b22c6bf5925448f725f90c6e0d8bf085819906520ddaa15aec58a6df5"}, +] + +[package.dependencies] +python-dateutil = ">=2.4" + +[[package]] +name = "flake8" +version = "7.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "flake8-blind-except" +version = "0.2.1" +description = "A flake8 extension that checks for blind except: statements" +optional = false +python-versions = "*" +files = [ + {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, +] + +[[package]] +name = "flake8-debugger" +version = "4.1.2" +description = "ipdb/pdb statement checker plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-isort" +version = "6.1.1" +description = "flake8 plugin that integrates isort" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_isort-6.1.1-py3-none-any.whl", hash = "sha256:0fec4dc3a15aefbdbe4012e51d5531a2eb5fa8b981cdfbc882296a59b54ede12"}, + {file = "flake8_isort-6.1.1.tar.gz", hash = "sha256:c1f82f3cf06a80c13e1d09bfae460e9666255d5c780b859f19f8318d420370b3"}, +] + +[package.dependencies] +flake8 = "*" +isort = ">=5.0.0,<6" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "flake8-quotes" +version = "3.4.0" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, +] + +[package.dependencies] +flake8 = "*" +setuptools = "*" + +[[package]] +name = "flask" +version = "3.0.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, +] + +[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 = "flask-caching" +version = "2.3.0" +description = "Adds caching support to Flask applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, + {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, +] + +[package.dependencies] +cachelib = ">=0.9.0,<0.10.0" +Flask = "*" + +[[package]] +name = "flask-cors" +version = "4.0.0" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Cors-4.0.0.tar.gz", hash = "sha256:f268522fcb2f73e2ecdde1ef45e2fd5c71cc48fe03cffb4b441c6d1b40684eb0"}, + {file = "Flask_Cors-4.0.0-py2.py3-none-any.whl", hash = "sha256:bc3492bfd6368d27cfe79c7821df5a8a319e1a6d5eab277a3794be19bdc51783"}, +] + +[package.dependencies] +Flask = ">=0.9" + +[[package]] +name = "flask-jwt-oidc" +version = "0.7.0" +description = "Opinionated flask oidc client" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +cachelib = "0.*" +Flask = ">=2" +python-jose = "^3.3.0" +six = "^1.16.0" + +[package.source] +type = "git" +url = "https://github.com/seeker25/flask-jwt-oidc.git" +reference = "HEAD" +resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" + +[[package]] +name = "flask-marshmallow" +version = "1.2.0" +description = "Flask + marshmallow for beautiful APIs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_marshmallow-1.2.0-py3-none-any.whl", hash = "sha256:ddd2a7c8db5e00a8d56c8ca5f651efae1de7d76b7d821b56ccc2caf09135ad12"}, + {file = "flask_marshmallow-1.2.0.tar.gz", hash = "sha256:d0f79eb9743f0c530a3d9e848503e1f2228e6b35a819c91e913af02e68421805"}, +] + +[package.dependencies] +Flask = ">=2.2" +marshmallow = ">=3.0.0" + +[package.extras] +dev = ["flask-marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["Sphinx (==7.2.6)", "marshmallow-sqlalchemy (>=0.19.0)", "sphinx-issues (==4.0.0)"] +sqlalchemy = ["flask-sqlalchemy (>=3.0.0)", "marshmallow-sqlalchemy (>=0.29.0)"] +tests = ["flask-marshmallow[sqlalchemy]", "pytest"] + +[[package]] +name = "flask-migrate" +version = "4.0.7" +description = "SQLAlchemy database migrations for Flask applications using Alembic." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Migrate-4.0.7.tar.gz", hash = "sha256:dff7dd25113c210b069af280ea713b883f3840c1e3455274745d7355778c8622"}, + {file = "Flask_Migrate-4.0.7-py3-none-any.whl", hash = "sha256:5c532be17e7b43a223b7500d620edae33795df27c75811ddf32560f7d48ec617"}, +] + +[package.dependencies] +alembic = ">=1.9.0" +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" + +[[package]] +name = "flask-moment" +version = "1.0.5" +description = "Formatting of dates and times in Flask templates using moment.js." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Moment-1.0.5.tar.gz", hash = "sha256:33307ecd4af8290b6df6a9828ff55ac0977d0567817f9bc0f69803d22ed2b55c"}, + {file = "Flask_Moment-1.0.5-py3-none-any.whl", hash = "sha256:6e7b3eef89e2137bbbee975405f241a68a44edfa34bf052c92d84364992adca6"}, +] + +[package.dependencies] +Flask = "*" +packaging = ">=14.1" + +[[package]] +name = "flask-opentracing" +version = "1.1.0" +description = "OpenTracing support for Flask applications" +optional = false +python-versions = "*" +files = [ + {file = "Flask-OpenTracing-1.1.0.tar.gz", hash = "sha256:a9a39d367fbe7e9ed9c77b90ac48159c1a3e82982a5abf84d3f4d710d24580ac"}, +] + +[package.dependencies] +Flask = "*" +opentracing = ">=2.0,<3" + +[package.extras] +tests = ["flake8", "flake8-quotes", "mock", "pytest", "pytest-cov"] + +[[package]] +name = "flask-script" +version = "2.0.6" +description = "Scripting support for Flask" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Script-2.0.6.tar.gz", hash = "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65"}, +] + +[package.dependencies] +Flask = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +description = "Add SQLAlchemy support to your Flask application." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[package.dependencies] +flask = ">=2.2.5" +sqlalchemy = ">=2.0.16" + +[[package]] +name = "freezegun" +version = "1.4.0" +description = "Let your Python tests travel through time" +optional = false +python-versions = ">=3.7" +files = [ + {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, + {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + +[[package]] +name = "gcp-queue" +version = "0.3.0" +description = "" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +flask = ">=1" +google-auth = "^2.28.2" +google-cloud-pubsub = "^2.20.2" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-connect-common.git" +reference = "main" +resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" +subdirectory = "python/gcp-queue" + +[[package]] +name = "google-api-core" +version = "2.17.1" +description = "Google API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, + {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-auth" +version = "2.29.0" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-cloud-pubsub" +version = "2.21.2" +description = "Google Cloud Pub/Sub API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, + {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<3.0.0dev" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +grpcio = ">=1.51.3,<2.0dev" +grpcio-status = ">=1.33.2" +proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +libcst = ["libcst (>=0.3.10)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.63.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, + {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.13.0" +description = "IAM API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.13.0.tar.gz", hash = "sha256:fad318608b9e093258fbf12529180f400d1c44453698a33509cc6ecf005b294e"}, + {file = "grpc_google_iam_v1-0.13.0-py2.py3-none-any.whl", hash = "sha256:53902e2af7de8df8c1bd91373d9be55b0743ec267a7428ea638db3775becae89"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.62.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, + {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, + {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, + {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, + {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, + {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, + {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, + {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, + {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, + {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, + {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, + {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, + {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, + {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, + {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, + {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, + {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, + {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, + {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, + {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, + {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, + {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, + {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, + {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.62.1)"] + +[[package]] +name = "grpcio-status" +version = "1.62.1" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, + {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.62.1" +protobuf = ">=4.21.6" + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "holidays" +version = "0.37" +description = "Generate and work with holidays in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "holidays-0.37-py3-none-any.whl", hash = "sha256:5b8ff8c94c06e3b225762d495e51b8e51205d332f8ad092aab809c4bffa8d123"}, + {file = "holidays-0.37.tar.gz", hash = "sha256:712df71a8d97b04554fa1c9208d219fbf174bad2864263bef24c6dcfa1ded6ff"}, +] + +[package.dependencies] +python-dateutil = "*" + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[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 = "jaeger-client" +version = "4.8.0" +description = "Jaeger Python OpenTracing Tracer implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaeger-client-4.8.0.tar.gz", hash = "sha256:3157836edab8e2c209bd2d6ae61113db36f7ee399e66b1dcbb715d87ab49bfe0"}, +] + +[package.dependencies] +opentracing = ">=2.1,<3.0" +threadloop = ">=1,<2" +thrift = "*" +tornado = ">=4.3" + +[package.extras] +tests = ["codecov", "coverage", "flake8", "flake8-quotes", "flake8-typing-imports", "mock", "mypy", "opentracing_instrumentation (>=3,<4)", "prometheus_client (==0.11.0)", "pycurl", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "pytest-localserver", "pytest-timeout", "pytest-tornado", "tchannel (==2.1.0)"] + +[[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 = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "launchdarkly-eventsource" +version = "1.1.1" +description = "LaunchDarkly SSE Client" +optional = false +python-versions = ">=3.7" +files = [ + {file = "launchdarkly_eventsource-1.1.1-py3-none-any.whl", hash = "sha256:3d7e5301bc4b4a744ecdaa10de8bce52c2f3c66a97e9aa10ab11ca81b67fb31b"}, + {file = "launchdarkly_eventsource-1.1.1.tar.gz", hash = "sha256:211791f1267f9b7b0a62a0bb5fc9c5ed1fb4a834440f16be551968dbe772557a"}, +] + +[package.dependencies] +urllib3 = ">=1.26.0,<3" + +[[package]] +name = "launchdarkly-server-sdk" +version = "8.2.1" +description = "LaunchDarkly SDK for Python" +optional = false +python-versions = "*" +files = [ + {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, + {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, +] + +[package.dependencies] +certifi = ">=2018.4.16" +expiringdict = ">=1.1.4" +pyRFC3339 = ">=1.0" +semver = ">=2.10.2" +urllib3 = ">=1.22.0,<3" + +[package.extras] +consul = ["python-consul (>=1.0.1)"] +dynamodb = ["boto3 (>=1.9.71)"] +redis = ["redis (>=2.10.5)"] + +[[package]] +name = "lovely-pytest-docker" +version = "0.3.1" +description = "Pytest testing utilities with docker containers." +optional = false +python-versions = "*" +files = [ + {file = "lovely-pytest-docker-0.3.1.tar.gz", hash = "sha256:4326a180bfd4dd4ad69c2ef3e3643c41075d965f40068488b40204602e6df85e"}, +] + +[package.dependencies] +pytest = "*" +six = "*" + +[[package]] +name = "mako" +version = "1.3.2" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, + {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "marshmallow" +version = "3.21.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-sqlalchemy" +version = "1.0.0" +description = "SQLAlchemy integration with the marshmallow (de)serialization library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow_sqlalchemy-1.0.0-py3-none-any.whl", hash = "sha256:f415d57809e3555b6323356589aba91e36e4470f35953d3a10c755ac5c3307df"}, + {file = "marshmallow_sqlalchemy-1.0.0.tar.gz", hash = "sha256:20a0f2fcdd5bddc86444fa01461f17f9b6a12a8ddd4ca8c9b34fe2f2e35d00a2"}, +] + +[package.dependencies] +marshmallow = ">=3.10.0" +SQLAlchemy = ">=1.4.40,<3.0" + +[package.extras] +dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)"] +tests = ["pytest (<8)", "pytest-lazy-fixture (>=0.6.2)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "minio" +version = "7.2.5" +description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" +optional = false +python-versions = "*" +files = [ + {file = "minio-7.2.5-py3-none-any.whl", hash = "sha256:ed9176c96d4271cb1022b9ecb8a538b1e55b32ae06add6de16425cab99ef2304"}, + {file = "minio-7.2.5.tar.gz", hash = "sha256:59d8906e2da248a9caac34d4958a859cc3a44abbe6447910c82b5abfa9d6a2e1"}, +] + +[package.dependencies] +argon2-cffi = "*" +certifi = "*" +pycryptodome = "*" +typing-extensions = "*" +urllib3 = "*" + +[[package]] +name = "more-itertools" +version = "10.2.0" +description = "More routines for operating on iterables, beyond itertools" +optional = false +python-versions = ">=3.8" +files = [ + {file = "more-itertools-10.2.0.tar.gz", hash = "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1"}, + {file = "more_itertools-10.2.0-py3-none-any.whl", hash = "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684"}, +] + +[[package]] +name = "opentracing" +version = "2.4.0" +description = "OpenTracing API for Python. See documentation at http://opentracing.io" +optional = false +python-versions = "*" +files = [ + {file = "opentracing-2.4.0.tar.gz", hash = "sha256:a173117e6ef580d55874734d1fa7ecb6f3655160b8b8974a2a1e98e5ec9c840d"}, +] + +[package.extras] +tests = ["Sphinx", "doubles", "flake8", "flake8-quotes", "gevent", "mock", "pytest", "pytest-cov", "pytest-mock", "six (>=1.10.0,<2.0)", "sphinx_rtd_theme", "tornado"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "paramiko" +version = "3.4.0" +description = "SSH2 protocol library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "paramiko-3.4.0-py3-none-any.whl", hash = "sha256:43f0b51115a896f9c00f59618023484cb3a14b98bbceab43394a39c6739b7ee7"}, + {file = "paramiko-3.4.0.tar.gz", hash = "sha256:aac08f26a31dc4dffd92821527d1682d99d52f9ef6851968114a8728f3c274d3"}, +] + +[package.dependencies] +bcrypt = ">=3.2" +cryptography = ">=3.3" +pynacl = ">=1.5" + +[package.extras] +all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +invoke = ["invoke (>=2.0)"] + +[[package]] +name = "pay-api" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.12" +files = [] +develop = false + +[package.dependencies] +alembic = "1.13.1" +attrs = "23.2.0" +blinker = "1.7.0" +cachelib = "0.9.0" +cachetools = "5.3.3" +cattrs = "23.2.3" +certifi = "2024.2.2" +cffi = "1.16.0" +charset-normalizer = "3.3.2" +click = "8.1.7" +croniter = "2.0.2" +cryptography = "42.0.5" +dpath = "2.1.6" +ecdsa = "0.18.0" +expiringdict = "1.2.2" +flask = "3.0.2" +flask-caching = "2.3.0" +flask-cors = "4.0.0" +flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} +flask-marshmallow = "1.2.0" +flask-migrate = "4.0.7" +flask-moment = "1.0.5" +flask-script = "2.0.6" +flask-sqlalchemy = "3.1.1" +gcp-queue = {git = "https://github.com/seeker25/sbc-connect-common.git", branch = "main", subdirectory = "python/gcp-queue"} +greenlet = "3.0.3" +gunicorn = "21.2.0" +holidays = "0.37" +idna = "3.6" +itsdangerous = "2.1.2" +jaeger-client = "4.8.0" +jinja2 = "3.1.3" +jsonschema = "4.17.3" +launchdarkly-eventsource = "1.1.1" +launchdarkly-server-sdk = "8.2.1" +mako = "1.3.2" +markupsafe = "2.1.5" +marshmallow = "3.21.1" +marshmallow-sqlalchemy = "1.0.0" +opentracing = "2.4.0" +packaging = "24.0" +pg8000 = "^1.30.5" +proto-plus = "1.23.0" +protobuf = "4.25.3" +psycopg2-binary = "2.9.9" +pyasn1 = "0.5.1" +pyasn1-modules = "0.3.0" +pycparser = "2.21" +pyhumps = "3.8.0" +pyrfc3339 = "1.1" +pyrsistent = "0.20.0" +python-dateutil = "2.9.0.post0" +python-dotenv = "1.0.1" +python-jose = "3.3.0" +pytz = "2024.1" +requests = "2.31.0" +rsa = "4.9" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +semver = "3.0.2" +sentry-sdk = "1.41.0" +six = "1.16.0" +sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} +sqlalchemy = "2.0.28" +sqlalchemy-utils = "0.41.1" +threadloop = "1.0.2" +thrift = "0.16.0" +tornado = "6.4" +typing-extensions = "4.10.0" +urllib3 = "2.2.1" +werkzeug = "3.0.1" + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-pay.git" +reference = "sync-python-to-main" +resolved_reference = "3cc0024430f298a460f4780bf8a55f6c84e1ec8e" +subdirectory = "pay-api" + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "pg8000" +version = "1.31.1" +description = "PostgreSQL interface library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pg8000-1.31.1-py3-none-any.whl", hash = "sha256:69aac9dba4114c9c8d0408232d54eaf7d06d271df7765caeed39960e057800e4"}, + {file = "pg8000-1.31.1.tar.gz", hash = "sha256:b11130d4c615dd3062ea8fed8143064a7978b7fe6d44f14b72261d43c8e27087"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.2" +scramp = ">=1.4.4" + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "proto-plus" +version = "1.23.0" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.23.0.tar.gz", hash = "sha256:89075171ef11988b3fa157f5dbd8b9cf09d65fffee97e29ce403cd8defba19d2"}, + {file = "proto_plus-1.23.0-py3-none-any.whl", hash = "sha256:a829c79e619e1cf632de091013a4173deed13a55f326ef84f05af6f50ff4c82c"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + +[[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 = "pyasn1" +version = "0.5.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pycryptodome" +version = "3.20.0" +description = "Cryptographic library for Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e"}, + {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pyhamcrest" +version = "2.1.0" +description = "Hamcrest framework for matcher objects" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyhamcrest-2.1.0-py3-none-any.whl", hash = "sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587"}, + {file = "pyhamcrest-2.1.0.tar.gz", hash = "sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c"}, +] + +[package.extras] +dev = ["black", "doc2dash", "flake8", "pyhamcrest[docs,tests]", "pytest-mypy", "towncrier", "tox", "tox-asdf", "twine"] +docs = ["alabaster (>=0.7,<1.0)", "sphinx (>=4.0,<5.0)"] +tests = ["coverage[toml]", "dataclasses", "mypy (!=0.940)", "pytest (>=5.0)", "pytest-mypy-plugins", "pytest-sugar", "pytest-xdist", "pyyaml", "types-dataclasses", "types-mock"] +tests-numpy = ["numpy", "pyhamcrest[tests]"] + +[[package]] +name = "pyhumps" +version = "3.8.0" +description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" +optional = false +python-versions = "*" +files = [ + {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, + {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, +] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint-flask" +version = "0.6" +description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" +optional = false +python-versions = "*" +files = [ + {file = "pylint-flask-0.6.tar.gz", hash = "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517"}, +] + +[package.dependencies] +pylint-plugin-utils = ">=0.2.1" + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + +[[package]] +name = "pynacl" +version = "1.5.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, +] + +[package.dependencies] +cffi = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] + +[[package]] +name = "pyrfc3339" +version = "1.1" +description = "Generate and parse RFC 3339 timestamps" +optional = false +python-versions = "*" +files = [ + {file = "pyRFC3339-1.1-py2.py3-none-any.whl", hash = "sha256:67196cb83b470709c580bb4738b83165e67c6cc60e1f2e4f286cfcb402a926f4"}, + {file = "pyRFC3339-1.1.tar.gz", hash = "sha256:81b8cbe1519cdb79bed04910dd6fa4e181faf8c88dff1e1b987b5f7ab23a5b1a"}, +] + +[package.dependencies] +pytz = "*" + +[[package]] +name = "pyrsistent" +version = "0.20.0" +description = "Persistent/Functional/Immutable data structures" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, + {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, + {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, + {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, + {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, + {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, + {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, +] + +[[package]] +name = "pysftp" +version = "0.2.9" +description = "A friendly face on SFTP" +optional = false +python-versions = "*" +files = [ + {file = "pysftp-0.2.9.tar.gz", hash = "sha256:fbf55a802e74d663673400acd92d5373c1c7ee94d765b428d9f977567ac4854a"}, +] + +[package.dependencies] +paramiko = ">=1.17" + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.23.6" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"}, + {file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"}, +] + +[package.dependencies] +pytest = ">=7.0.0,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, +] + +[package.dependencies] +pytest = ">=6.2.5" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +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.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "sbc_common_components" +version = "0.0.0" +description = "" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +flask = "*" +flask-jwt-oidc = ">=0.1.5" +Flask-OpenTracing = "1.1.0" +Flask-SQLAlchemy = "*" +jaeger-client = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-common-components.git" +reference = "HEAD" +resolved_reference = "22978d810dc4e85c51c3129936686b0a17124e64" +subdirectory = "python" + +[[package]] +name = "scramp" +version = "1.4.4" +description = "An implementation of the SCRAM protocol." +optional = false +python-versions = ">=3.7" +files = [ + {file = "scramp-1.4.4-py3-none-any.whl", hash = "sha256:b142312df7c2977241d951318b7ee923d6b7a4f75ba0f05b621ece1ed616faa3"}, + {file = "scramp-1.4.4.tar.gz", hash = "sha256:b7022a140040f33cf863ab2657917ed05287a807b917950489b89b9f685d59bc"}, +] + +[package.dependencies] +asn1crypto = ">=1.5.1" + +[[package]] +name = "semver" +version = "3.0.2" +description = "Python helper for Semantic Versioning (https://semver.org)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "semver-3.0.2-py3-none-any.whl", hash = "sha256:b1ea4686fe70b981f85359eda33199d60c53964284e0cfb4977d243e37cf4bf4"}, + {file = "semver-3.0.2.tar.gz", hash = "sha256:6253adb39c70f6e51afed2fa7152bcd414c411286088fb4b9effb133885ab4cc"}, +] + +[[package]] +name = "sentry-sdk" +version = "1.41.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.41.0.tar.gz", hash = "sha256:4f2d6c43c07925d8cd10dfbd0970ea7cb784f70e79523cca9dbcd72df38e5a46"}, + {file = "sentry_sdk-1.41.0-py2.py3-none-any.whl", hash = "sha256:be4f8f4b29a80b6a3b71f0f31487beb9e296391da20af8504498a328befed53f"}, +] + +[package.dependencies] +certifi = "*" +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "69.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "simple-cloudevent" +version = "0.0.2" +description = "A short description of the project" +optional = false +python-versions = ">=3.8" +files = [] +develop = false + +[package.dependencies] +strict-rfc3339 = "*" + +[package.source] +type = "git" +url = "https://github.com/daxiom/simple-cloudevent.py.git" +reference = "HEAD" +resolved_reference = "447cabb988202206ac69e71177d7cd11b6c0b002" + +[[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 = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sql-versioning" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.10" +files = [] +develop = false + +[package.source] +type = "git" +url = "https://github.com/bcgov/lear.git" +reference = "feature-legal-name" +resolved_reference = "2d4c389743ff307be74af1861d6debda8bc8d194" +subdirectory = "python/common/sql-versioning" + +[[package]] +name = "sqlalchemy" +version = "2.0.28" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "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\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlalchemy-utils" +version = "0.41.1" +description = "Various utility functions for SQLAlchemy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "SQLAlchemy-Utils-0.41.1.tar.gz", hash = "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74"}, + {file = "SQLAlchemy_Utils-0.41.1-py3-none-any.whl", hash = "sha256:6c96b0768ea3f15c0dc56b363d386138c562752b84f647fb8d31a2223aaab801"}, +] + +[package.dependencies] +SQLAlchemy = ">=1.3" + +[package.extras] +arrow = ["arrow (>=0.3.4)"] +babel = ["Babel (>=1.3)"] +color = ["colour (>=0.0.4)"] +encrypted = ["cryptography (>=0.6)"] +intervals = ["intervals (>=0.7.1)"] +password = ["passlib (>=1.6,<2.0)"] +pendulum = ["pendulum (>=2.0.5)"] +phone = ["phonenumbers (>=5.9.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +timezone = ["python-dateutil"] +url = ["furl (>=0.4.1)"] + +[[package]] +name = "strict-rfc3339" +version = "0.7" +description = "Strict, simple, lightweight RFC3339 functions" +optional = false +python-versions = "*" +files = [ + {file = "strict-rfc3339-0.7.tar.gz", hash = "sha256:5cad17bedfc3af57b399db0fed32771f18fc54bbd917e85546088607ac5e1277"}, +] + +[[package]] +name = "threadloop" +version = "1.0.2" +description = "Tornado IOLoop Backed Concurrent Futures" +optional = false +python-versions = "*" +files = [ + {file = "threadloop-1.0.2-py2-none-any.whl", hash = "sha256:5c90dbefab6ffbdba26afb4829d2a9df8275d13ac7dc58dccb0e279992679599"}, + {file = "threadloop-1.0.2.tar.gz", hash = "sha256:8b180aac31013de13c2ad5c834819771992d350267bddb854613ae77ef571944"}, +] + +[package.dependencies] +tornado = "*" + +[[package]] +name = "thrift" +version = "0.16.0" +description = "Python bindings for the Apache Thrift RPC system" +optional = false +python-versions = "*" +files = [ + {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, +] + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[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.12" +content-hash = "b16efd0364fcd29610e0eae102b66eda5771f66116953175e2d4e54550537cfa" diff --git a/jobs/payment-jobs/pyproject.toml b/jobs/payment-jobs/pyproject.toml new file mode 100644 index 000000000..402cfb107 --- /dev/null +++ b/jobs/payment-jobs/pyproject.toml @@ -0,0 +1,58 @@ +[tool.poetry] +name = "payment-jobs" +version = "0.1.0" +description = "" +authors = ["Travis Semple "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", branch = "sync-python-to-main", subdirectory = "pay-api"} +gunicorn = "^21.2.0" +flask = "^3.0.2" +flask-sqlalchemy = "^3.1.1" +sqlalchemy = "^2.0.28" +flask-marshmallow = "^1.2.0" +marshmallow-sqlalchemy = "^1.0.0" +python-dotenv = "^1.0.1" +psycopg2-binary = "^2.9.9" +jsonschema = "4.17.3" +requests = "^2.31.0" +werkzeug = "^3.0.1" +jaeger-client = "^4.8.0" +minio = "^7.2.5" +pysftp = "^0.2.9" +flask-migrate = "^4.0.7" +itsdangerous = "^2.1.2" +dataclass-wizard = "^0.22.3" +launchdarkly-server-sdk = "^8.2.1" +cx-oracle = "^8.3.0" +more-itertools = "^10.2.0" +pg8000 = "^1.30.5" + + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pytest-mock = "^3.12.0" +requests = "^2.31.0" +pyhamcrest = "^2.1.0" +pytest-cov = "^4.1.0" +faker = "^24.2.0" +flake8 = "^7.0.0" +flake8-blind-except = "^0.2.1" +flake8-debugger = "^4.1.2" +flake8-docstrings = "^1.7.0" +flake8-isort = "^6.1.1" +flake8-quotes = "^3.4.0" +pep8-naming = "^0.13.3" +autopep8 = "^2.0.4" +coverage = "^7.4.3" +pylint = "^3.1.0" +pylint-flask = "^0.6" +freezegun = "^1.4.0" +lovely-pytest-docker = "^0.3.1" +pytest-asyncio = "^0.23.5.post1" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/jobs/payment-jobs/requirements.txt b/jobs/payment-jobs/requirements.txt deleted file mode 100644 index 814a98534..000000000 --- a/jobs/payment-jobs/requirements.txt +++ /dev/null @@ -1,94 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git@94986110a7f6c7ba4f57ed8b038101ba7d864a94#egg=sbc_common_components&subdirectory=python --e git+https://github.com/seeker25/sbc-connect-common.git@cf0d1d76e37ecb9b051393f6e9f4a419177ee2b4#egg=gcp_queue&subdirectory=python/gcp-queue --e git+https://github.com/seeker25/sbc-pay.git@67bf51103557104dd093b89e7a3a1cd625b5efd6#egg=pay_api&subdirectory=pay-api -Flask-Caching==2.3.0 -Flask-Cors==3.0.10 -Flask-Migrate==2.7.0 -Flask-Moment==1.0.5 -Flask-OpenTracing==1.1.0 -Flask-SQLAlchemy==2.5.1 -Flask-Script==2.0.6 -Flask==1.1.2 -Jinja2==3.0.3 -Mako==1.3.5 -MarkupSafe==2.1.5 -PyNaCl==1.5.0 -SQLAlchemy-Continuum==1.3.15 -SQLAlchemy-Utils==0.41.2 -SQLAlchemy==1.3.24 -Werkzeug==1.0.1 -alembic==1.13.1 -argon2-cffi-bindings==21.2.0 -argon2-cffi==23.1.0 -attrs==23.2.0 -bcrypt==4.1.3 -blinker==1.8.2 -cachelib==0.9.0 -cachetools==5.3.3 -cattrs==23.2.3 -certifi==2024.2.2 -cffi==1.16.0 -charset-normalizer==3.3.2 -click==8.1.7 -croniter==2.0.5 -cryptography==42.0.7 -cx-Oracle==8.3.0 -dataclass-wizard==0.22.3 -dpath==2.1.6 -ecdsa==0.19.0 -exceptiongroup==1.2.1 -expiringdict==1.2.2 -flask-jwt-oidc==0.3.0 -flask-marshmallow==0.11.0 -google-api-core==2.19.0 -google-auth==2.29.0 -google-cloud-pubsub==2.21.1 -googleapis-common-protos==1.63.0 -grpc-google-iam-v1==0.13.0 -grpcio-status==1.48.2 -grpcio==1.64.0 -gunicorn==22.0.0 -holidays==0.37 -idna==3.7 -importlib_metadata==7.1.0 -importlib_resources==6.4.0 -itsdangerous==2.0.1 -jaeger-client==4.8.0 -jsonschema==4.17.3 -launchdarkly-server-sdk==8.2.1 -marshmallow-sqlalchemy==0.25.0 -marshmallow==3.21.2 -minio==7.2.7 -more-itertools==10.2.0 -opentracing==2.4.0 -packaging==24.0 -paramiko==3.4.0 -pkgutil_resolve_name==1.3.10 -proto-plus==1.23.0 -protobuf==3.19.6 -psycopg2-binary==2.9.9 -pyRFC3339==1.1 -pyasn1==0.6.0 -pyasn1_modules==0.4.0 -pycparser==2.22 -pycryptodome==3.20.0 -pyhumps==3.8.0 -pyrsistent==0.20.0 -pysftp==0.2.9 -python-dateutil==2.9.0.post0 -python-dotenv==1.0.1 -python-jose==3.3.0 -pytz==2024.1 -requests==2.32.2 -rsa==4.9 -semver==3.0.2 -sentry-sdk==2.3.1 -# simple-cloudevent @ git+https://github.com/daxiom/simple-cloudevent.py.git@447cabb988202206ac69e71177d7cd11b6c0b002 -six==1.16.0 -strict-rfc3339==0.7 -threadloop==1.0.2 -thrift==0.20.0 -tornado==6.4 -typing_extensions==4.12.0 -urllib3==2.2.1 -zipp==3.19.0 diff --git a/jobs/payment-jobs/requirements/bcregistry-libraries.txt b/jobs/payment-jobs/requirements/bcregistry-libraries.txt deleted file mode 100644 index a388c1fce..000000000 --- a/jobs/payment-jobs/requirements/bcregistry-libraries.txt +++ /dev/null @@ -1,3 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/seeker25/sbc-pay.git@refactor_queues#egg=pay_api&subdirectory=pay-api --e git+https://github.com/seeker25/sbc-connect-common.git@small_tweaks#egg=gcp-queue&subdirectory=python/gcp-queue diff --git a/jobs/payment-jobs/requirements/dev.txt b/jobs/payment-jobs/requirements/dev.txt deleted file mode 100644 index 0fe0a755f..000000000 --- a/jobs/payment-jobs/requirements/dev.txt +++ /dev/null @@ -1,28 +0,0 @@ -# Everything the developer needs in addition to the production requirements --r prod.txt - -# Testing -pytest -pytest-mock -requests -pyhamcrest -pytest-cov -Faker - -# Lint and code style -flake8==5.0.4 -flake8-blind-except -flake8-debugger -flake8-docstrings -flake8-isort -flake8-quotes -pep8-naming -autopep8 -coverage -pylint -pylint-flask -FreezeGun - -# docker -lovely-pytest-docker -pytest-asyncio diff --git a/jobs/payment-jobs/requirements/prod.txt b/jobs/payment-jobs/requirements/prod.txt deleted file mode 100644 index 23b4e5302..000000000 --- a/jobs/payment-jobs/requirements/prod.txt +++ /dev/null @@ -1,20 +0,0 @@ -gunicorn -Flask -Flask-SQLAlchemy -SQLAlchemy<1.4 -flask-marshmallow==0.11.0 -marshmallow-sqlalchemy==0.25.0 -python-dotenv -psycopg2-binary -jsonschema==4.17.3 -requests -Werkzeug<2 -jaeger-client -minio -pysftp -Flask-Migrate<3 -itsdangerous==2.0.1 -dataclass_wizard -launchdarkly-server-sdk==8.2.1 -cx_Oracle -more_itertools diff --git a/jobs/payment-jobs/services/oracle.py b/jobs/payment-jobs/services/oracle.py index 08c52095a..6ffc99d80 100644 --- a/jobs/payment-jobs/services/oracle.py +++ b/jobs/payment-jobs/services/oracle.py @@ -16,8 +16,8 @@ These will get initialized by the application. """ import cx_Oracle -from flask import _app_ctx_stack, current_app - +from flask import current_app +from flask.globals import app_ctx class OracleDB: """Oracle database connection object for re-use in application.""" @@ -40,7 +40,7 @@ def init_app(self, app): def teardown(ctx=None): """Oracle session pool cleans up after itself.""" if not ctx: - ctx = _app_ctx_stack.top + ctx = app_ctx if hasattr(ctx, '_oracle_pool'): ctx._oracle_pool.close() # pylint: disable=protected-access @@ -81,7 +81,7 @@ def connection(self): # pylint: disable=inconsistent-return-statements and then return an acquired session :return: cx_Oracle.connection type """ - ctx = _app_ctx_stack.top + ctx = app_ctx if ctx is not None: if not hasattr(ctx, '_oracle_pool'): ctx._oracle_pool = self._create_pool() # pylint: disable = protected-access; need this method diff --git a/jobs/payment-jobs/setup.cfg b/jobs/payment-jobs/setup.cfg index fa9785f04..f4f4f4e4e 100755 --- a/jobs/payment-jobs/setup.cfg +++ b/jobs/payment-jobs/setup.cfg @@ -17,7 +17,7 @@ keywords = [options] zip_safe = True -python_requires = >=3.6 +python_requires = >=3.12 include_package_data = True packages = find: diff --git a/jobs/payment-jobs/tasks/ap_task.py b/jobs/payment-jobs/tasks/ap_task.py index 81f34208a..c317cb447 100644 --- a/jobs/payment-jobs/tasks/ap_task.py +++ b/jobs/payment-jobs/tasks/ap_task.py @@ -23,12 +23,12 @@ from pay_api.models import DistributionCode as DistributionCodeModel from pay_api.models import EjvFile as EjvFileModel from pay_api.models import EjvHeader as EjvHeaderModel -from pay_api.models import EjvInvoiceLink as EjvInvoiceLinkModel +from pay_api.models import EjvLink as EjvLinkModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import Refund as RefundModel from pay_api.models import RoutingSlip as RoutingSlipModel from pay_api.models import db -from pay_api.utils.enums import DisbursementStatus, EjvFileType, RoutingSlipStatus +from pay_api.utils.enums import DisbursementStatus, EjvFileType, EJVLinkType, RoutingSlipStatus from tasks.common.cgi_ap import CgiAP from tasks.common.dataclasses import APLine from tasks.ejv_partner_distribution_task import EjvPartnerDistributionTask @@ -156,9 +156,10 @@ def _create_non_gov_disbursement_file(cls): # pylint:disable=too-many-locals ap_content = f'{ap_content}{batch_trailer}' for inv in invoices: - db.session.add(EjvInvoiceLinkModel(invoice_id=inv.id, - ejv_header_id=ejv_header_model.id, - disbursement_status_code=DisbursementStatus.UPLOADED.value)) + db.session.add(EjvLinkModel(link_id=inv.id, + link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header_model.id, + disbursement_status_code=DisbursementStatus.UPLOADED.value)) inv.disbursement_status_code = DisbursementStatus.UPLOADED.value db.session.flush() diff --git a/jobs/payment-jobs/tasks/cfs_create_account_task.py b/jobs/payment-jobs/tasks/cfs_create_account_task.py index 580bf03fb..55dbf497e 100644 --- a/jobs/payment-jobs/tasks/cfs_create_account_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_account_task.py @@ -21,7 +21,7 @@ from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.services.cfs_service import CFSService from pay_api.services.oauth_service import OAuthService -from pay_api.utils.constants import RECEIPT_METHOD_PAD_DAILY +from pay_api.utils.constants import RECEIPT_METHOD_EFT_MONTHLY, RECEIPT_METHOD_PAD_DAILY from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, PaymentMethod from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message @@ -74,13 +74,6 @@ def _get_account_contact(cls, auth_token: str, auth_account_id: str): @classmethod def _create_cfs_account(cls, pending_account: CfsAccountModel, pay_account: PaymentAccountModel, auth_token: str): - # If PAD Account creation in CFS is paused, then just continue - # TODO Remove once PAD account bugs are fixed and stable on CAS side. - if current_app.config.get('CFS_STOP_PAD_ACCOUNT_CREATION') and \ - pay_account.payment_method == PaymentMethod.PAD.value: - current_app.logger.info('Continuing to next record as CFS PAD account creation is stopped.') - return - current_app.logger.info( f'Creating pay system instance for {pay_account.payment_method} for account {pay_account.id}.') @@ -103,7 +96,11 @@ def _create_cfs_account(cls, pending_account: CfsAccountModel, pay_account: Paym 'bankAccountName': pay_account.name } - if pending_account.cfs_account and pending_account.cfs_party and pending_account.cfs_site: + if pay_account.payment_method == PaymentMethod.EFT.value: + cfs_account_details = CFSService.create_cfs_account(identifier=pay_account.auth_account_id, + contact_info=contact_info, + receipt_method=RECEIPT_METHOD_EFT_MONTHLY) + elif pending_account.cfs_account and pending_account.cfs_party and pending_account.cfs_site: # This means, PAD account details have changed. So update banking details for this CFS account bank_details = CFSService.update_bank_details(name=pay_account.auth_account_id, party_number=pending_account.cfs_party, diff --git a/jobs/payment-jobs/tasks/cfs_create_invoice_task.py b/jobs/payment-jobs/tasks/cfs_create_invoice_task.py index c101a712a..862d2fce7 100644 --- a/jobs/payment-jobs/tasks/cfs_create_invoice_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_invoice_task.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Task to create CFS invoices offline.""" -from datetime import datetime +from datetime import datetime, timezone from decimal import Decimal import time from typing import List @@ -26,6 +26,7 @@ from pay_api.models import Receipt as ReceiptModel from pay_api.models import RoutingSlip as RoutingSlipModel from pay_api.models import db +from pay_api.services import EftService from pay_api.services.cfs_service import CFSService from pay_api.services.invoice_reference import InvoiceReference from pay_api.services.payment import Payment @@ -58,13 +59,15 @@ def create_invoices(cls): current_app.logger.info('<< Starting PAD Invoice Creation') cls._create_pad_invoices() current_app.logger.info('>> Done PAD Invoice Creation') + + current_app.logger.info('<< Starting EFT Invoice Creation') + cls._create_eft_invoices() + current_app.logger.info('>> Done EFT Invoice Creation') + current_app.logger.info('<< Starting Online Banking Invoice Creation') cls._create_online_banking_invoices() current_app.logger.info('>> Done Online Banking Invoice Creation') - if current_app.config.get('DISABLE_CFS_FAS_INTEGRATION'): - return - # Cancel invoice is the only non-creation of invoice in this job. current_app.logger.info('<< Starting CANCEL Routing Slip Invoices') cls._cancel_rs_invoices() @@ -240,16 +243,13 @@ def _create_pad_invoices(cls): # pylint: disable=too-many-locals current_app.logger.info(f'Found {len(pad_accounts)} with PAD transactions.') - invoice_ref_subquery = db.session.query(InvoiceReferenceModel.invoice_id). \ - filter(InvoiceReferenceModel.status_code.in_((InvoiceReferenceStatus.ACTIVE.value,))) - for account in pad_accounts: # Find all PAD invoices for this account account_invoices = db.session.query(InvoiceModel) \ .filter(InvoiceModel.payment_account_id == account.id) \ .filter(InvoiceModel.payment_method_code == PaymentMethod.PAD.value) \ .filter(InvoiceModel.invoice_status_code == InvoiceStatus.APPROVED.value) \ - .filter(InvoiceModel.id.notin_(invoice_ref_subquery)) \ + .filter(InvoiceModel.id.notin_(cls._active_invoice_reference_subquery())) \ .order_by(InvoiceModel.created_on.desc()).all() # Get cfs account @@ -335,6 +335,132 @@ def _create_pad_invoices(cls): # pylint: disable=too-many-locals invoice.cfs_account_id = cfs_account.id db.session.commit() + @classmethod + def _return_eft_accounts(cls): + """Return EFT accounts.""" + invoice_subquery = db.session.query(InvoiceModel.payment_account_id) \ + .filter(InvoiceModel.payment_method_code == PaymentMethod.EFT.value) \ + .filter(InvoiceModel.invoice_status_code == InvoiceStatus.APPROVED.value).subquery() + + eft_accounts: List[PaymentAccountModel] = db.session.query(PaymentAccountModel) \ + .join(CfsAccountModel, CfsAccountModel.account_id == PaymentAccountModel.id) \ + .filter(CfsAccountModel.status != CfsAccountStatus.FREEZE.value) \ + .filter(PaymentAccountModel.id.in_(invoice_subquery)).all() + + current_app.logger.info(f'Found {len(eft_accounts)} with EFT transactions.') + + return eft_accounts + + @classmethod + def _save_invoice_reference_records(cls, account_invoices, cfs_account, invoice_response): + """Save invoice reference records.""" + for invoice in account_invoices: + + invoice_reference = EftService.create_invoice_reference( + invoice=invoice, + invoice_number=invoice_response.get('invoice_number'), + reference_number=invoice_response.get('pbc_ref_number', None) + ) + db.session.add(invoice_reference) + + invoice.cfs_account_id = cfs_account.id + db.session.commit() + + @classmethod + def _active_invoice_reference_subquery(cls): + return db.session.query(InvoiceReferenceModel.invoice_id). \ + filter(InvoiceReferenceModel.status_code.in_((InvoiceReferenceStatus.ACTIVE.value,))) + + @classmethod + def _create_eft_invoices(cls): + """Create EFT invoices in CFS.""" + eft_accounts = cls._return_eft_accounts() + + for eft_account in eft_accounts: + account_invoices = db.session.query(InvoiceModel) \ + .filter(InvoiceModel.payment_account_id == eft_account.id) \ + .filter(InvoiceModel.payment_method_code == PaymentMethod.EFT.value) \ + .filter(InvoiceModel.invoice_status_code == InvoiceStatus.APPROVED.value) \ + .filter(InvoiceModel.id.notin_(cls._active_invoice_reference_subquery())) \ + .order_by(InvoiceModel.created_on.desc()).all() + + if not account_invoices: + continue + + payment_account: PaymentAccountService = PaymentAccountService.find_by_id(eft_account.id) + + if not payment_account: + continue + + current_app.logger.debug( + f'Found {len(account_invoices)} invoices for account {payment_account.auth_account_id}') + + cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(payment_account.id) + + # If no CFS account then the payment method might have changed from EFT to DRAWDOWN + if not cfs_account: + cfs_account: CfsAccountModel = CfsAccountModel.query.\ + filter(CfsAccountModel.account_id == payment_account.id).order_by(CfsAccountModel.id.desc()).first() + + # If CFS account is not ACTIVE or INACTIVE (for above case), raise error and continue + if cfs_account.status not in (CfsAccountStatus.ACTIVE.value, CfsAccountStatus.INACTIVE.value): + current_app.logger.info(f'CFS status for account {payment_account.auth_account_id} ' + f'is {payment_account.cfs_account_status} skipping.') + continue + + # Add all payment line items together + lines = [] + invoice_total = Decimal('0') + for invoice in account_invoices: + lines.extend(invoice.payment_line_items) + invoice_total += invoice.total + + invoice_number = account_invoices[-1].id + try: + # Get the first invoice id as the trx number for CFS + invoice_response = CFSService.create_account_invoice(transaction_number=invoice_number, + line_items=lines, + cfs_account=cfs_account) + except Exception as e: # NOQA # pylint: disable=broad-except + # There is a chance that the error is a timeout from CAS side, + # so to make sure we are not missing any data, make a GET call for the invoice we tried to create + # and use it if it got created. + current_app.logger.info(e) # INFO is intentional as sentry alerted only after the following try/catch + has_invoice_created: bool = False + try: + # add a 10 seconds delay here as safe bet, as CFS takes time to create the invoice + time.sleep(10) + invoice_number = generate_transaction_number(str(invoice_number)) + invoice_response = CFSService.get_invoice( + cfs_account=cfs_account, inv_number=invoice_number + ) + has_invoice_created = invoice_response.get('invoice_number', None) == invoice_number + invoice_total_matches = Decimal(invoice_response.get('total', '0')) == invoice_total + except Exception as exc: # NOQA # pylint: disable=broad-except,unused-variable + # Ignore this error, as it is irrelevant and error on outer level is relevant. + pass + # If no invoice is created raise an error for sentry + if not has_invoice_created: + capture_message(f'Error on creating EFT invoice: account id={payment_account.id}, ' + f'auth account : {payment_account.auth_account_id}, ERROR : {str(e)}', + level='error') + current_app.logger.error(e) + continue + if not invoice_total_matches: + capture_message(f'Error on creating EFT invoice: account id={payment_account.id}, ' + f'auth account : {payment_account.auth_account_id}, Invoice exists: ' + f' CAS total: {invoice_response.get("total", 0)}, PAY-BC total: {invoice_total}', + level='error') + current_app.logger.error(e) + continue + + mailer.publish_mailer_events(QueueMessageTypes.EFT_INVOICE_CREATED.value, payment_account, { + 'invoice_total': float(invoice_total), + 'invoice_process_date': f'{datetime.now(tz=timezone.utc)}' + }) + + cls._save_invoice_reference_records(account_invoices, cfs_account, invoice_response) + @classmethod def _create_online_banking_invoices(cls): """Create online banking invoices to CFS system.""" diff --git a/jobs/payment-jobs/tasks/eft_transfer_task.py b/jobs/payment-jobs/tasks/eft_transfer_task.py deleted file mode 100644 index 8b62c952e..000000000 --- a/jobs/payment-jobs/tasks/eft_transfer_task.py +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright © 2023 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Task to create EFT Transfer Journal Voucher.""" - -import time -from datetime import datetime -from typing import List - -from flask import current_app -from pay_api.models import DistributionCode as DistributionCodeModel -from pay_api.models import EFTGLTransfer as EFTGLTransferModel -from pay_api.models import EFTShortnames as EFTShortnameModel -from pay_api.models import EjvFile as EjvFileModel -from pay_api.models import EjvHeader as EjvHeaderModel -from pay_api.models import EjvInvoiceLink as EjvInvoiceLinkModel -from pay_api.models import Invoice as InvoiceModel -from pay_api.models import PaymentAccount as PaymentAccountModel -from pay_api.models import PaymentLineItem as PaymentLineItemModel -from pay_api.models import db -from pay_api.services.flags import flags -from pay_api.utils.enums import DisbursementStatus, EFTGlTransferType, EjvFileType, InvoiceStatus, PaymentMethod -from sqlalchemy import exists, func - -from tasks.common.cgi_ejv import CgiEjv - - -class EftTransferTask(CgiEjv): - """Task to create EJV Files.""" - - @classmethod - def create_ejv_file(cls): - """Create JV files and upload to CGI. - - Steps: - 1. Find all invoices from invoice table for EFT Transfer. - 2. Group by fee schedule and create JV Header and JV Details. - 3. Upload the file to minio for future reference. - 4. Upload to sftp for processing. First upload JV file and then a TRG file. - 5. Update the statuses and create records to for the batch. - """ - eft_enabled = flags.is_on('enable-eft-payment-method', default=False) - if eft_enabled: - cls._create_ejv_file_for_eft_transfer() - - @staticmethod - def get_invoices_for_transfer(payment_account_id: int): - """Return invoices for EFT Holdings transfer.""" - # Return all EFT Paid invoices that don't already have an EFT GL Transfer record - invoices: List[InvoiceModel] = db.session.query(InvoiceModel) \ - .filter(InvoiceModel.invoice_status_code == InvoiceStatus.PAID.value) \ - .filter(InvoiceModel.payment_method_code == PaymentMethod.EFT.value) \ - .filter(InvoiceModel.payment_account_id == payment_account_id) \ - .filter(~exists().where((EFTGLTransferModel.invoice_id == InvoiceModel.id) & - (EFTGLTransferModel.transfer_type == EFTGlTransferType.TRANSFER.value))).all() - return invoices - - @staticmethod - def get_invoices_for_refund_reversal(payment_account_id: int): - """Return invoices for EFT reversal.""" - refund_inv_statuses = (InvoiceStatus.REFUNDED.value, InvoiceStatus.REFUND_REQUESTED.value, - InvoiceStatus.CREDITED.value) - # Future may need to re-evaluate when EFT Short name unlinking use cases are defined - invoices: List[InvoiceModel] = db.session.query(InvoiceModel) \ - .filter(InvoiceModel.invoice_status_code.in_(refund_inv_statuses)) \ - .filter(InvoiceModel.payment_method_code == PaymentMethod.EFT.value) \ - .filter(InvoiceModel.payment_account_id == payment_account_id) \ - .filter(InvoiceModel.disbursement_status_code == DisbursementStatus.COMPLETED.value) \ - .filter(~exists().where((EFTGLTransferModel.invoice_id == InvoiceModel.id) & - (EFTGLTransferModel.transfer_type == EFTGlTransferType.REVERSAL.value))).all() - current_app.logger.info(invoices) - return invoices - - @staticmethod - def get_account_ids() -> List[int]: - """Return account IDs for EFT payments.""" - return db.session.query(func.DISTINCT(InvoiceModel.payment_account_id)) \ - .filter(InvoiceModel.invoice_status_code == InvoiceStatus.PAID.value) \ - .filter(InvoiceModel.payment_method_code == PaymentMethod.EFT.value) \ - .filter(~exists().where((EFTGLTransferModel.invoice_id == InvoiceModel.id) & - (EFTGLTransferModel.transfer_type == EFTGlTransferType.TRANSFER.value))).all() - - @staticmethod - def create_eft_gl_transfer(eft_holding_gl: str, line_distribution_gl: str, transfer_type: str, - line_item: PaymentLineItemModel, payment_account: PaymentAccountModel): - """Create EFT GL Transfer record.""" - short_name_id = db.session.query(EFTShortnameModel.id) \ - .filter(EFTShortnameModel.auth_account_id == payment_account.auth_account_id).one() - source_gl = eft_holding_gl if transfer_type == EFTGlTransferType.TRANSFER.value else line_distribution_gl - target_gl = line_distribution_gl if transfer_type == EFTGlTransferType.TRANSFER.value else eft_holding_gl - now = datetime.now() - return EFTGLTransferModel( - invoice_id=line_item.invoice_id, - is_processed=True, - processed_on=now, - short_name_id=short_name_id, - source_gl=source_gl.strip(), - target_gl=target_gl.strip(), - transfer_amount=line_item.total, - transfer_type=transfer_type, - transfer_date=now - ) - - @classmethod - def _process_eft_transfer_invoices(cls, invoices: [InvoiceModel], transfer_type: str, - eft_gl_transfers: dict = None) -> [EFTGLTransferModel]: - """Create EFT GL Transfer for invoice line items.""" - eft_holding_gl = current_app.config.get('EFT_HOLDING_GL') - eft_gl_transfers = eft_gl_transfers or {} - - for invoice in invoices: - payment_account = PaymentAccountModel.find_by_id(invoice.payment_account_id) - for line_item in invoice.payment_line_items: - distribution_code: DistributionCodeModel = \ - DistributionCodeModel.find_by_id(line_item.fee_distribution_id) - - # Create line distribution transfer - line_distribution_code: DistributionCodeModel = DistributionCodeModel.find_by_id( - distribution_code.distribution_code_id - ) - - line_distribution = cls.get_distribution_string(line_distribution_code) - - line_gl_transfer = cls.create_eft_gl_transfer( - eft_holding_gl=eft_holding_gl, - line_distribution_gl=line_distribution, - transfer_type=transfer_type, - line_item=line_item, - payment_account=payment_account - ) - - eft_gl_transfers.setdefault(invoice.payment_account_id, []) - eft_gl_transfers[invoice.payment_account_id].append(line_gl_transfer) - db.session.add(line_gl_transfer) - - # Check for service fee, if there is one create a transfer record - if distribution_code.service_fee_distribution_code_id: - service_fee_distribution_code: DistributionCodeModel = DistributionCodeModel.find_by_id( - distribution_code.service_fee_distribution_code_id - ) - - service_fee_distribution = cls.get_distribution_string(service_fee_distribution_code) - - service_fee_gl_transfer = cls.create_eft_gl_transfer( - eft_holding_gl=eft_holding_gl, - line_distribution_gl=service_fee_distribution, - transfer_type=transfer_type, - line_item=line_item, - payment_account=payment_account - ) - service_fee_gl_transfer.transfer_amount = line_item.service_fees - eft_gl_transfers[invoice.payment_account_id].append(service_fee_gl_transfer) - db.session.add(service_fee_gl_transfer) - - return eft_gl_transfers - - @staticmethod - def process_invoice_ejv_links(invoices: [InvoiceModel], ejv_header_model_id: int): - """Create EJV Invoice Links.""" - current_app.logger.info('Creating ejv invoice link records and setting invoice status.') - sequence = 1 - for inv in invoices: - current_app.logger.debug(f'Creating EJV Invoice Link for invoice id: {inv.id}') - # Create Ejv file link and flush - ejv_invoice_link = EjvInvoiceLinkModel(invoice_id=inv.id, ejv_header_id=ejv_header_model_id, - disbursement_status_code=DisbursementStatus.UPLOADED.value, - sequence=sequence) - db.session.add(ejv_invoice_link) - sequence += 1 - - @classmethod - def _create_ejv_file_for_eft_transfer(cls): # pylint:disable=too-many-locals, too-many-statements - """Create EJV file for the EFT Transfer and upload.""" - ejv_content: str = '' - batch_total: float = 0 - control_total: int = 0 - today = datetime.now() - transfer_desc = current_app.config.get('EFT_TRANSFER_DESC'). \ - format(today.strftime('%B').upper(), f'{today.day:0>2}')[:100] - transfer_desc = f'{transfer_desc:<100}' - - # Create a ejv file model record. - ejv_file_model: EjvFileModel = EjvFileModel( - file_type=EjvFileType.TRANSFER.value, - file_ref=cls.get_file_name(), - disbursement_status_code=DisbursementStatus.UPLOADED.value - ).flush() - batch_number = cls.get_batch_number(ejv_file_model.id) - batch_type = 'GA' - - account_ids = cls.get_account_ids() - - # JV Batch Header - batch_header: str = cls.get_batch_header(batch_number, batch_type) - - effective_date: str = cls.get_effective_date() - for account_id in account_ids: - account_jv: str = '' - payment_invoices = cls.get_invoices_for_transfer(account_id) - refund_invoices = cls.get_invoices_for_refund_reversal(account_id) - transfers = cls._process_eft_transfer_invoices(payment_invoices, EFTGlTransferType.TRANSFER.value) - cls._process_eft_transfer_invoices(refund_invoices, EFTGlTransferType.REVERSAL.value, transfers) - invoices = payment_invoices + refund_invoices - - ejv_header_model: EjvFileModel = EjvHeaderModel( - payment_account_id=account_id, - disbursement_status_code=DisbursementStatus.UPLOADED.value, - ejv_file_id=ejv_file_model.id - ).flush() - journal_name: str = cls.get_journal_name(ejv_header_model.id) - - line_number: int = 0 - total: float = 0 - - current_app.logger.info(f'Processing EFT Transfers for account_id: {account_id}.') - account_transfers: List[EFTGLTransferModel] = transfers[account_id[0]] - - for eft_transfer in account_transfers: - invoice_number = f'#{eft_transfer.invoice_id}' - description = transfer_desc[:-len(invoice_number)] + invoice_number - description = f'{description[:100]:<100}' - - if eft_transfer.transfer_amount > 0: - total += eft_transfer.transfer_amount - flow_through = f'{eft_transfer.invoice_id:<110}' - - line_number += 1 - control_total += 1 - - # Debit from source gl - source_gl = f'{eft_transfer.source_gl}{cls.EMPTY:<16}' - target_gl = f'{eft_transfer.target_gl}{cls.EMPTY:<16}' - - account_jv = account_jv + cls.get_jv_line(batch_type, source_gl, description, - effective_date, flow_through, journal_name, - eft_transfer.transfer_amount, - line_number, 'D') - # Credit to target gl - account_jv = account_jv + cls.get_jv_line(batch_type, target_gl, description, - effective_date, flow_through, journal_name, - eft_transfer.transfer_amount, - line_number, 'C') - line_number += 1 - control_total += 1 - - batch_total += total - - # Skip if we have no total from the transfers. - if total > 0: - # A JV header for each account. - control_total += 1 - account_jv = cls.get_jv_header(batch_type, cls.get_journal_batch_name(batch_number), - journal_name, total) + account_jv - ejv_content = ejv_content + account_jv - - # Create ejv invoice link records and set invoice status - cls.process_invoice_ejv_links(invoices, ejv_header_model.id) - - db.session.flush() - - if not ejv_content: - db.session.rollback() - return - - # JV Batch Trailer - batch_trailer: str = cls.get_batch_trailer(batch_number, batch_total, batch_type, control_total) - ejv_content = f'{batch_header}{ejv_content}{batch_trailer}' - - # Create a file add this content. - file_path_with_name, trg_file_path = cls.create_inbox_and_trg_files(ejv_content) - - # Upload file and trg to FTP - current_app.logger.info('Uploading EFT Transfer file to ftp.') - cls.upload(ejv_content, cls.get_file_name(), file_path_with_name, trg_file_path) - - db.session.commit() - - # Add a sleep to prevent collision on file name. - time.sleep(1) diff --git a/jobs/payment-jobs/tasks/ejv_partner_distribution_task.py b/jobs/payment-jobs/tasks/ejv_partner_distribution_task.py index 0fde9b178..0ec8b13c1 100644 --- a/jobs/payment-jobs/tasks/ejv_partner_distribution_task.py +++ b/jobs/payment-jobs/tasks/ejv_partner_distribution_task.py @@ -23,13 +23,14 @@ from pay_api.models import DistributionCodeLink as DistributionCodeLinkModel from pay_api.models import EjvFile as EjvFileModel from pay_api.models import EjvHeader as EjvHeaderModel -from pay_api.models import EjvInvoiceLink as EjvInvoiceLinkModel +from pay_api.models import EjvLink as EjvLinkModel from pay_api.models import FeeSchedule as FeeScheduleModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentLineItem as PaymentLineItemModel +from pay_api.models import RefundsPartial as RefundsPartialModel from pay_api.models import Receipt as ReceiptModel from pay_api.models import db -from pay_api.utils.enums import DisbursementStatus, EjvFileType, InvoiceStatus, PaymentMethod +from pay_api.utils.enums import DisbursementStatus, EjvFileType, EJVLinkType, InvoiceStatus, PaymentMethod from sqlalchemy import Date, cast from tasks.common.cgi_ejv import CgiEjv @@ -68,6 +69,21 @@ def get_invoices_for_disbursement(partner): current_app.logger.info(invoices) return invoices + @staticmethod + def get_refund_partial_payment_line_items_for_disbursement(partner) -> List[PaymentLineItemModel]: + """Return payment line items with partial refunds for disbursement.""" + payment_line_items: List[PaymentLineItemModel] = db.session.query(PaymentLineItemModel) \ + .join(InvoiceModel, PaymentLineItemModel.invoice_id == InvoiceModel.id) \ + .join(RefundsPartialModel, PaymentLineItemModel.id == RefundsPartialModel.payment_line_item_id) \ + .filter(InvoiceModel.invoice_status_code == InvoiceStatus.PAID.value) \ + .filter(InvoiceModel.payment_method_code.in_([PaymentMethod.DIRECT_PAY.value])) \ + .filter((RefundsPartialModel.disbursement_status_code.is_(None)) | + (RefundsPartialModel.disbursement_status_code == DisbursementStatus.ERRORED.value)) \ + .filter(InvoiceModel.corp_type_code == partner.code) \ + .all() + current_app.logger.info(payment_line_items) + return payment_line_items + @classmethod def get_invoices_for_refund_reversal(cls, partner): """Return invoices for refund reversal.""" @@ -113,12 +129,16 @@ def _create_ejv_file_for_partner(cls, batch_type: str): # pylint:disable=too-ma for partner in partners: # Find all invoices for the partner to disburse. - # This includes invoices which are not PAID and invoices which are refunded. + # This includes invoices which are not PAID and invoices which are refunded and partial refunded. payment_invoices = cls.get_invoices_for_disbursement(partner) refund_reversals = cls.get_invoices_for_refund_reversal(partner) invoices = payment_invoices + refund_reversals + + # Process partial refunds for each partner + refund_partial_items = cls.get_refund_partial_payment_line_items_for_disbursement(partner) + # If no invoices continue. - if not invoices: + if not invoices and not refund_partial_items: continue effective_date: str = cls.get_effective_date() @@ -134,11 +154,16 @@ def _create_ejv_file_for_partner(cls, batch_type: str): # pylint:disable=too-ma # and create one JV Header and detail for each. distribution_code_set = set() invoice_id_list = [] + partial_line_item_id_list = [] for inv in invoices: invoice_id_list.append(inv.id) for line_item in inv.payment_line_items: distribution_code_set.add(line_item.fee_distribution_id) + for line_item in refund_partial_items: + partial_line_item_id_list.append(line_item.id) + distribution_code_set.add(line_item.fee_distribution_id) + for distribution_code_id in list(distribution_code_set): distribution_code: DistributionCodeModel = DistributionCodeModel.find_by_id(distribution_code_id) credit_distribution_code: DistributionCodeModel = DistributionCodeModel.find_by_id( @@ -147,13 +172,22 @@ def _create_ejv_file_for_partner(cls, batch_type: str): # pylint:disable=too-ma if credit_distribution_code.stop_ejv: continue - line_items = cls._find_line_items_by_invoice_and_distribution(distribution_code_id, invoice_id_list) + line_items = cls._find_line_items_by_invoice_and_distribution( + distribution_code_id, invoice_id_list) + + refund_partial_items = cls._find_refund_partial_items_by_distribution( + distribution_code_id, partial_line_item_id_list) total: float = 0 for line in line_items: total += line.total + partial_refund_total: float = 0 + for refund_partial in refund_partial_items: + partial_refund_total += refund_partial.refund_amount + batch_total += total + batch_total += partial_refund_total debit_distribution = cls.get_distribution_string(distribution_code) # Debit from BCREG GL credit_distribution = cls.get_distribution_string(credit_distribution_code) # Credit to partner GL @@ -193,20 +227,39 @@ def _create_ejv_file_for_partner(cls, batch_type: str): # pylint:disable=too-ma control_total += 1 + partial_refund_number: int = 0 + for refund_partial in refund_partial_items: + # JV Details for partial refunds + partial_refund_number += 1 + # Flow Through add it as the refunds_partial id. + flow_through = f'{refund_partial.id:<110}' + refund_partial_number = f'#{refund_partial.id}' + description = disbursement_desc[:-len(refund_partial_number)] + refund_partial_number + description = f'{description[:100]:<100}' + + ejv_content = '{}{}'.format(ejv_content, # pylint:disable=consider-using-f-string + cls.get_jv_line(batch_type, credit_distribution, description, + effective_date, flow_through, journal_name, + refund_partial.refund_amount, + partial_refund_number, 'D')) + partial_refund_number += 1 + control_total += 1 + + # Add a line here for debit too + ejv_content = '{}{}'.format(ejv_content, # pylint:disable=consider-using-f-string + cls.get_jv_line(batch_type, debit_distribution, description, + effective_date, flow_through, journal_name, + refund_partial.refund_amount, + partial_refund_number, 'C')) + control_total += 1 + + # Update partial refund status + refund_partial.disbursement_status_code = DisbursementStatus.UPLOADED.value + + # Create ejv invoice/partial_refund link records and set invoice status sequence = 1 - # Create ejv invoice link records and set invoice status - for inv in invoices: - # Create Ejv file link and flush - link_model = EjvInvoiceLinkModel(invoice_id=inv.id, - ejv_header_id=ejv_header_model.id, - disbursement_status_code=DisbursementStatus.UPLOADED.value, - sequence=sequence) - # Set distribution status to invoice - db.session.add(link_model) - sequence += 1 - inv.disbursement_status_code = DisbursementStatus.UPLOADED.value - - db.session.flush() + sequence = cls._create_ejv_link(invoices, ejv_header_model, sequence, EJVLinkType.INVOICE.value) + cls._create_ejv_link(refund_partial_items, ejv_header_model, sequence, EJVLinkType.REFUND.value) if not ejv_content: db.session.rollback() @@ -238,6 +291,18 @@ def _find_line_items_by_invoice_and_distribution(cls, distribution_code_id, invo .filter(PaymentLineItemModel.fee_distribution_id == distribution_code_id) return line_items + @classmethod + def _find_refund_partial_items_by_distribution(cls, distribution_code_id, partial_line_item_id_list) \ + -> List[RefundsPartialModel]: + """Find and return all payment line items for this distribution.""" + line_items: List[RefundsPartialModel] = db.session.query(RefundsPartialModel) \ + .join(PaymentLineItemModel, PaymentLineItemModel.id == RefundsPartialModel.payment_line_item_id) \ + .filter(RefundsPartialModel.payment_line_item_id.in_(partial_line_item_id_list)) \ + .filter(RefundsPartialModel.refund_amount > 0) \ + .filter(PaymentLineItemModel.fee_distribution_id == distribution_code_id) \ + .all() + return line_items + @classmethod def _get_partners_by_batch_type(cls, batch_type) -> List[CorpTypeModel]: """Return partners by batch type.""" @@ -251,22 +316,38 @@ def _get_partners_by_batch_type(cls, batch_type) -> List[CorpTypeModel]: if batch_type == 'GA': # Rule for GA. Credit is 112 and debit is 112. - partner_distribution_code_ids: List[int] = query.filter( + partner_distribution_code_ids: List[int] = db.session.scalars(query.filter( DistributionCodeModel.client == bc_reg_client_code - ).all() + )).all() else: # Rule for GI. Debit is 112 and credit is not 112. - partner_distribution_code_ids: List[int] = query.filter( + partner_distribution_code_ids: List[int] = db.session.scalars(query.filter( DistributionCodeModel.client != bc_reg_client_code - ).all() + )).all() # Find all distribution codes who have these partner distribution codes as disbursement. - fee_distribution_codes: List[int] = db.session.query(DistributionCodeModel.distribution_code_id).filter( - DistributionCodeModel.disbursement_distribution_code_id.in_(partner_distribution_code_ids)).all() + fee_query = db.session.query(DistributionCodeModel.distribution_code_id).filter( + DistributionCodeModel.disbursement_distribution_code_id.in_(partner_distribution_code_ids)) + fee_distribution_codes: List[int] = db.session.scalars(fee_query).all() - corp_type_codes: List[str] = db.session.query(FeeScheduleModel.corp_type_code). \ + corp_type_query = db.session.query(FeeScheduleModel.corp_type_code). \ join(DistributionCodeLinkModel, - DistributionCodeLinkModel.fee_schedule_id == FeeScheduleModel.fee_schedule_id). \ - filter(DistributionCodeLinkModel.distribution_code_id.in_(fee_distribution_codes)).all() + DistributionCodeLinkModel.fee_schedule_id == FeeScheduleModel.fee_schedule_id).\ + filter(DistributionCodeLinkModel.distribution_code_id.in_(fee_distribution_codes)) + corp_type_codes: List[str] = db.session.scalars(corp_type_query).all() return db.session.query(CorpTypeModel).filter(CorpTypeModel.code.in_(corp_type_codes)).all() + + @classmethod + def _create_ejv_link(cls, items, ejv_header_model, sequence, link_type): + for item in items: + link_model = EjvLinkModel(link_id=item.id, + link_type=link_type, + ejv_header_id=ejv_header_model.id, + disbursement_status_code=DisbursementStatus.UPLOADED.value, + sequence=sequence) + db.session.add(link_model) + sequence += 1 + item.disbursement_status_code = DisbursementStatus.UPLOADED.value + db.session.flush() + return sequence diff --git a/jobs/payment-jobs/tasks/ejv_payment_task.py b/jobs/payment-jobs/tasks/ejv_payment_task.py index 55c15f716..22ae8a172 100644 --- a/jobs/payment-jobs/tasks/ejv_payment_task.py +++ b/jobs/payment-jobs/tasks/ejv_payment_task.py @@ -20,12 +20,13 @@ from pay_api.models import DistributionCode as DistributionCodeModel from pay_api.models import EjvFile as EjvFileModel from pay_api.models import EjvHeader as EjvHeaderModel -from pay_api.models import EjvInvoiceLink as EjvInvoiceLinkModel +from pay_api.models import EjvLink as EjvLinkModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import InvoiceReference as InvoiceReferenceModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import db -from pay_api.utils.enums import DisbursementStatus, EjvFileType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod +from pay_api.utils.enums import ( + DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod) from pay_api.utils.util import generate_transaction_number from tasks.common.cgi_ejv import CgiEjv @@ -180,10 +181,10 @@ def _create_ejv_file_for_gov_account(cls, batch_type: str): # pylint:disable=to sequence = 1 for inv in invoices: current_app.logger.debug(f'Creating EJV Invoice Link for invoice id: {inv.id}') - # Create Ejv file link and flush - ejv_invoice_link = EjvInvoiceLinkModel(invoice_id=inv.id, ejv_header_id=ejv_header_model.id, - disbursement_status_code=DisbursementStatus.UPLOADED.value, - sequence=sequence) + ejv_invoice_link = EjvLinkModel(link_id=inv.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header_model.id, + disbursement_status_code=DisbursementStatus.UPLOADED.value, + sequence=sequence) db.session.add(ejv_invoice_link) sequence += 1 # Set distribution status to invoice @@ -226,18 +227,18 @@ def _get_account_ids_for_payment(cls, batch_type) -> List[int]: """Return account IDs for payment.""" # CREDIT : Distribution code against fee schedule # DEBIT : Distribution code against account. - bc_reg_client_code = current_app.config.get('CGI_BCREG_CLIENT_CODE') # 112 #TODO + bc_reg_client_code = current_app.config.get('CGI_BCREG_CLIENT_CODE') query = db.session.query(DistributionCodeModel.account_id) \ .filter(DistributionCodeModel.stop_ejv.is_(False) | DistributionCodeModel.stop_ejv.is_(None)) \ .filter(DistributionCodeModel.account_id.isnot(None)) if batch_type == 'GA': # Rule for GA. Credit is 112 and debit is 112. For BCREG client code is 112 - account_ids: List[int] = query.filter(DistributionCodeModel.client == bc_reg_client_code).all() + account_ids: List[int] = query.filter(DistributionCodeModel.client == bc_reg_client_code) else: # Rule for GI. Credit is 112 and debit is not 112. For BCREG client code is 112 - account_ids: List[int] = query.filter(DistributionCodeModel.client != bc_reg_client_code).all() - return account_ids + account_ids: List[int] = query.filter(DistributionCodeModel.client != bc_reg_client_code) + return db.session.scalars(account_ids).all() @classmethod def _get_invoices_for_payment(cls, account_id: int) -> List[InvoiceModel]: diff --git a/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py b/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py new file mode 100644 index 000000000..73ff70168 --- /dev/null +++ b/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py @@ -0,0 +1,281 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Task for linking electronic funds transfers.""" +from dataclasses import dataclass +from datetime import datetime +from typing import List + +from flask import current_app +from pay_api.models import CfsAccount as CfsAccountModel +from pay_api.models import EFTCredit as EFTCreditModel +from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel +from pay_api.models import EFTShortnames as EFTShortNamesModel +from pay_api.models import Invoice as InvoiceModel +from pay_api.models import InvoiceReference as InvoiceReferenceModel +from pay_api.models import Payment as PaymentModel +from pay_api.models import PaymentAccount as PaymentAccountModel +from pay_api.models import PaymentLineItem as PaymentLineItemModel +from pay_api.models import Receipt as ReceiptModel +from pay_api.models import db +from pay_api.services import EFTShortNamesService +from pay_api.services.cfs_service import CFSService +from pay_api.services.receipt import Receipt +from pay_api.utils.enums import ( + CfsAccountStatus, EFTShortnameStatus, InvoiceReferenceStatus, InvoiceStatus, ReverseOperation) +from sentry_sdk import capture_message + + +@dataclass +class EFTShortnameInfo: + """Consolidated EFT Short name information for processing.""" + + id: int + auth_account_id: str + version: int + + +class ElectronicFundsTransferTask: # pylint:disable=too-few-public-methods + """Task to link electronic funds transfers.""" + + @classmethod + def link_electronic_funds_transfers(cls): + """Create invoice in CFS. + + Steps: + 1. Find all pending EFT Short Names in a LINKED state. + 2. Receipts created in CAS representing the EFT. + 3. Apply the receipts to the invoices. + 4. Notify mailer + """ + eft_short_names: List[EFTShortnameInfo] = cls._get_eft_short_names_by_status(EFTShortnameStatus.LINKED.value) + for eft_short_name in eft_short_names: + try: + current_app.logger.debug(f'Linking Electronic Funds Transfer: {eft_short_name.id}') + payment_account: PaymentAccountModel = PaymentAccountModel.find_by_auth_account_id( + eft_short_name.auth_account_id) + cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(payment_account.id) + eft_credit: EFTCreditModel = EFTCreditModel.find_by_payment_account_id(payment_account.id) + + payment = db.session.query(PaymentModel) \ + .join(PaymentAccountModel, PaymentAccountModel.id == PaymentModel.payment_account_id) \ + .join(EFTCreditModel, EFTCreditModel.payment_account_id == PaymentAccountModel.id) \ + .join(EFTShortNamesModel, EFTShortNamesModel.id == EFTCreditModel.short_name_id) \ + .filter(EFTShortNamesModel.id == eft_short_name.id).first() + + receipt_number = payment.id + + CFSService.create_cfs_receipt( + cfs_account=cfs_account, + rcpt_number=receipt_number, + rcpt_date=payment.payment_date.strftime('%Y-%m-%d'), + amount=payment.invoice_amount, + payment_method=payment_account.payment_method, + access_token=CFSService.get_fas_token().json().get('access_token')) + + # apply receipt to cfs_account + total_invoice_amount = cls._apply_electronic_funds_transfers_to_pending_invoices( + eft_short_name, receipt_number) + current_app.logger.debug(f'Total Invoice Amount : {total_invoice_amount}') + + eft_credit.remaining_amount = eft_credit.amount - total_invoice_amount + + eft_short_name.save() + + except Exception as e: # NOQA # pylint: disable=broad-except + capture_message( + f'Error on Electronics Funds Transfer ID:={eft_short_name.id}, ' + f'electronic funds transfer : {eft_short_name.id}, ERROR : {str(e)}', level='error') + current_app.logger.error(e) + continue + + @classmethod + def unlink_electronic_funds_transfers(cls): + """Unlink electronic funds transfers.""" + eft_short_names = cls._get_eft_short_names_by_status(EFTShortnameStatus.UNLINKED.value) + for eft_short_name in eft_short_names: + try: + current_app.logger.debug(f'Unlinking Electronic Funds Transfer: {eft_short_name.id}') + payment_account: PaymentAccountModel = PaymentAccountModel.find_by_auth_account_id( + eft_short_name.auth_account_id) + cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(payment_account.id) + eft_credit: EFTCreditModel = EFTCreditModel.find_by_payment_account_id(payment_account.id) + + payment = db.session.query(PaymentModel) \ + .join(PaymentAccountModel, PaymentAccountModel.id == PaymentModel.payment_account_id) \ + .join(EFTCreditModel, EFTCreditModel.payment_account_id == PaymentAccountModel.id) \ + .join(EFTShortNamesModel, EFTShortNamesModel.id == EFTCreditModel.short_name_id) \ + .filter(EFTShortNamesModel.id == eft_short_name.id).first() + + eft_short_name.version += 1 + + receipt_number = f'{payment.id}R{eft_short_name.version}' + + CFSService.reverse_rs_receipt_in_cfs(cfs_account, receipt_number, ReverseOperation.CORRECTION.value) + + CFSService.create_cfs_receipt( + cfs_account=cfs_account, + rcpt_number=f'R{receipt_number}', + rcpt_date=payment.payment_date.strftime('%Y-%m-%d'), + amount=payment.invoice_amount, + payment_method=payment_account.payment_method, + access_token=CFSService.get_fas_token().json().get('access_token')) + + cls._reset_invoices_and_references_to_created_for_electronic_funds_transfer(eft_short_name) + + cls._apply_electronic_funds_transfers_to_pending_invoices(eft_short_name, receipt_number) + + eft_credit.remaining_amount = eft_credit.amount + + eft_short_name.save() + + except Exception as e: # NOQA # pylint: disable=broad-except + capture_message( + f'Error on Processing UNLINKING for :={receipt_number}, ' + f'EFT Short Name : {eft_short_name.id}, ERROR : {str(e)}', level='error') + current_app.logger.error(e) + continue + + @classmethod + def _get_eft_short_names_by_status(cls, status: str) -> List[EFTShortNamesModel]: + """Get electronic funds transfer by state.""" + query = db.session.query(EFTShortNamesModel.id.label('short_name_id'), EFTShortnameLinksModel.auth_account_id, + EFTShortNamesModel.version.label('short_name_version'), ) \ + .join(EFTShortnameLinksModel, EFTShortnameLinksModel.eft_short_name_id == EFTShortNamesModel.id) \ + .join(PaymentAccountModel, PaymentAccountModel.auth_account_id == EFTShortnameLinksModel.auth_account_id) \ + .join(CfsAccountModel, CfsAccountModel.account_id == PaymentAccountModel.id) \ + .filter(CfsAccountModel.status == CfsAccountStatus.ACTIVE.value) + + if status == EFTShortnameStatus.LINKED.value: + query = query.filter(EFTShortnameLinksModel.status_code == status) + + result = query.all() + short_name_results = [] + + # Short name can have multiple linked accounts, prepare list of dataclasses with the associated + # auth_account_ids for the outer processing loops + for short_name_id, auth_account_id, short_name_version in result: + short_name_results.append(EFTShortnameInfo(id=short_name_id, auth_account_id=auth_account_id, + version=short_name_version)) + + return short_name_results + + @classmethod + def _apply_electronic_funds_transfers_to_pending_invoices(cls, + eft_short_name: EFTShortNamesModel, + receipt_number) -> float: + """Apply the electronic funds transfers again.""" + current_app.logger.info( + f'Applying electronic funds transfer to pending invoices: {eft_short_name.id}') + + payment_account: PaymentAccountModel = PaymentAccountModel.find_by_auth_account_id( + eft_short_name.auth_account_id) + + cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(payment_account.id) + + invoices: List[InvoiceModel] = EFTShortNamesService.get_invoices_owing( + eft_short_name.auth_account_id) + + current_app.logger.info(f'Found {len(invoices)} to apply receipt') + applied_amount = 0 + for invoice in invoices: + invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel.find_by_invoice_id_and_status( + invoice.id, InvoiceReferenceStatus.ACTIVE.value + ) + + # apply invoice to the CFS_ACCOUNT + cls.apply_eft_receipt_to_invoice( + payment_account, cfs_account, eft_short_name, invoice, invoice_reference.invoice_number, receipt_number + ) + # IF invoice balance is zero, then update records. + if CFSService.get_invoice(cfs_account=cfs_account, inv_number=invoice_reference.invoice_number) \ + .get('amount_due') == 0: + applied_amount += invoice.total + invoice_reference.status_code = InvoiceReferenceStatus.COMPLETED.value + invoice.invoice_status_code = InvoiceStatus.PAID.value + invoice.payment_date = datetime.now() + + return applied_amount + + @classmethod + def _reset_invoices_and_references_to_created_for_electronic_funds_transfer(cls, + eft_short_name: EFTShortNamesModel): + """Reset Invoices, Invoice references and Receipts for EFT.""" + invoices: List[InvoiceModel] = db.session.query(InvoiceModel) \ + .join(InvoiceReferenceModel, PaymentModel.invoice_number == InvoiceReferenceModel.invoice_number) \ + .join(InvoiceModel, InvoiceReferenceModel.invoice_id == InvoiceModel.id) \ + .join(PaymentLineItemModel, PaymentLineItemModel.invoice_id == InvoiceModel.id) \ + .join(PaymentAccountModel, PaymentAccountModel.id == InvoiceModel.payment_account_id) \ + .join(EFTCreditModel, EFTCreditModel.payment_account_id == PaymentAccountModel.id) \ + .join(EFTShortNamesModel, EFTShortNamesModel.id == EFTCreditModel.short_name_id) \ + .join(EFTShortnameLinksModel, EFTShortnameLinksModel.eft_short_name_id == EFTShortNamesModel.id) \ + .filter(InvoiceModel.invoice_status_code == InvoiceStatus.PAID.value) \ + .filter(EFTShortNamesModel.auth_account_id == eft_short_name.auth_account_id) \ + .all() + + for invoice in invoices: + # Reset Invoice and Invoice Reference statuses. + invoice.invoice_status_code = InvoiceStatus.CREATED.value + invoice_reference = InvoiceReferenceModel.find_by_invoice_id_and_status( + invoice.id, InvoiceReferenceStatus.COMPLETED.value + ) + invoice_reference.status_code = InvoiceReferenceStatus.ACTIVE.value + # Delete receipts as they are now reversed in CFS. + for receipt in ReceiptModel.find_all_receipts_for_invoice(invoice.id): + db.session.delete(receipt) + + @classmethod + def apply_eft_receipt_to_invoice(cls, # pylint: disable = too-many-arguments, too-many-locals + payment_account: PaymentAccountModel, + cfs_account: CfsAccountModel, + eft_short_name: EFTShortNamesModel, + invoice: InvoiceModel, + invoice_number: str, + receipt_number) -> bool: + """Apply electronic funds transfers (receipts in CFS) to invoice.""" + has_errors = False + # an invoice has to be applied to multiple receipts (incl. all linked RS); apply till the balance is zero + try: + current_app.logger.debug(f'Apply receipt {receipt_number} on invoice {invoice_number} ' + f'for electronic funds transfer {eft_short_name.id}') + + # If balance of receipt is zero, continue to next receipt. + receipt_balance_before_apply = float( + CFSService.get_receipt(cfs_account, receipt_number).get('unapplied_amount') + ) + current_app.logger.debug(f'Current balance on {receipt_number} = {receipt_balance_before_apply}') + if receipt_balance_before_apply == 0: + current_app.logger.debug(f'Applying receipt {receipt_number} to {invoice_number}') + receipt_response = CFSService.apply_receipt(cfs_account, receipt_number, invoice_number) + + receipt = Receipt() + receipt.receipt_number = receipt_response.json().get('receipt_number', None) + receipt_amount = receipt_balance_before_apply - float(receipt_response.json().get('unapplied_amount')) + receipt.receipt_amount = receipt_amount + receipt.invoice_id = invoice.id + receipt.receipt_date = datetime.now() + receipt.flush() + + invoice_from_cfs = CFSService.get_invoice(cfs_account, invoice_number) + if invoice_from_cfs.get('amount_due') == 0: + has_errors = True + return has_errors + + except Exception as e: # NOQA # pylint: disable=broad-except + capture_message( + f'Error on creating electronic funds transfer invoice: account id={payment_account.id}, ' + f'electronic funds transfer : {eft_short_name.id}, ERROR : {str(e)}', level='error') + current_app.logger.error(e) + has_errors = True + + return has_errors diff --git a/jobs/payment-jobs/tasks/statement_notification_task.py b/jobs/payment-jobs/tasks/statement_notification_task.py index 25fee2a62..927bd368c 100644 --- a/jobs/payment-jobs/tasks/statement_notification_task.py +++ b/jobs/payment-jobs/tasks/statement_notification_task.py @@ -116,7 +116,7 @@ def send_email(cls, token, recipients: str, """Send the email asynchronously, using the given details.""" subject = 'Your BC Registries statement is available' current_app.logger.info(f'send_email to recipients: {recipients}') - notify_url = current_app.config.get('NOTIFY_API_URL') + '/notify/' + notify_url = current_app.config.get('NOTIFY_API_ENDPOINT') + 'notify/' notify_body = { 'recipients': recipients, 'content': { diff --git a/jobs/payment-jobs/tests/docker/nginx.conf b/jobs/payment-jobs/tests/docker/nginx.conf index d88123d4f..ad848249b 100644 --- a/jobs/payment-jobs/tests/docker/nginx.conf +++ b/jobs/payment-jobs/tests/docker/nginx.conf @@ -24,7 +24,7 @@ http { set $last_path_component example1; rewrite ^/reports-api/api/v1/(.*) /$1 break; proxy_set_header Prefer example=$last_path_component; - proxy_set_header Accept "application/json"; #TODO + proxy_set_header Accept "application/json"; proxy_pass http://reports:4010/; } diff --git a/jobs/payment-jobs/tests/jobs/conftest.py b/jobs/payment-jobs/tests/jobs/conftest.py index 2e44e6064..832ed837f 100644 --- a/jobs/payment-jobs/tests/jobs/conftest.py +++ b/jobs/payment-jobs/tests/jobs/conftest.py @@ -15,31 +15,52 @@ """Common setup and fixtures for the py-test suite used by this service.""" import os -import sys import time import pytest from flask_migrate import Migrate, upgrade from pay_api.models import db as _db from sqlalchemy import event, text -from sqlalchemy.schema import DropConstraint, MetaData +from sqlalchemy_utils import create_database, database_exists, drop_database from invoke_jobs import create_app from utils.logger import setup_logging +@pytest.fixture(autouse=True) +def mock_pub_sub_call(mocker): + """Mock pub sub call.""" + class Expando(object): + """Expando class.""" + + class PublisherMock: + """Publisher Mock.""" + + def __init__(self, *args, **kwargs): + def result(): + """Return true for mock.""" + return True + self.result = result + + def publish(self, *args, **kwargs): + """Publish mock.""" + ex = Expando() + ex.result = self.result + return ex + + mocker.patch('google.cloud.pubsub_v1.PublisherClient', PublisherMock) + + @pytest.fixture(scope='session') def app(): """Return a session-wide application configured in TEST mode.""" - _app = create_app('testing') - return _app + return create_app('testing') @pytest.fixture(scope='function') def app_request(): """Return a session-wide application configured in TEST mode.""" - _app = create_app('testing') - return _app + return create_app('testing') @pytest.fixture(scope='session') @@ -55,107 +76,57 @@ def client_ctx(app): yield _client -@pytest.fixture(scope='session') +@pytest.fixture(scope='session', autouse=True) def db(app): # pylint: disable=redefined-outer-name, invalid-name - """Return a session-wide initialised database. - - Drops all existing tables - Meta follows Postgres FKs - """ + """Return a session-wide initialised database.""" with app.app_context(): - # Clear out views - view_sql = """SELECT table_name FROM information_schema.views - WHERE table_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(view_sql))]: - try: - sess.execute(text('DROP VIEW public.%s ;' % seq)) - print('DROP VIEW public.%s ' % seq) - except Exception as err: # NOQA pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # Clear out any existing tables - metadata = MetaData(_db.engine) - metadata.reflect() - for table in metadata.tables.values(): - for fk in table.foreign_keys: # pylint: disable=invalid-name - _db.engine.execute(DropConstraint(fk.constraint)) - metadata.drop_all() - _db.drop_all() - - sequence_sql = """SELECT sequence_name FROM information_schema.sequences - WHERE sequence_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(sequence_sql))]: - try: - sess.execute(text('DROP SEQUENCE public.%s ;' % seq)) - print('DROP SEQUENCE public.%s ' % seq) - except Exception as err: # NOQA # pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # ############################################ - # There are 2 approaches, an empty database, or the same one that the app will use - # create the tables - # _db.create_all() - # or - # Use Alembic to load all of the DB revisions including supporting lookup data - # This is the path we'll use in legal_api!! - - # even though this isn't referenced directly, it sets up the internal configs that upgrade needs - migrations_path = [folder for folder in sys.path if 'pay-api/pay-api' in folder] - if len(migrations_path) > 0: - migrations_path = migrations_path[0].replace('/pay-api/src', '/pay-api/migrations') - # Fix for windows. - else: - migrations_path = os.path.abspath('../../pay-api/migrations') - - Migrate(app, _db, directory=migrations_path) + if database_exists(_db.engine.url): + drop_database(_db.engine.url) + create_database(_db.engine.url) + _db.session().execute(text('SET TIME ZONE "UTC";')) + pay_api_dir = os.path.abspath('..').replace('jobs', 'pay-api') + pay_api_dir = os.path.join(pay_api_dir, 'migrations') + Migrate(app, _db, directory=pay_api_dir) upgrade() - # Restore the logging, alembic and sqlalchemy have their own logging from alembic.ini. setup_logging(os.path.abspath('logging.conf')) return _db -@pytest.fixture(scope='function') -def session(app, db): # pylint: disable=redefined-outer-name, invalid-name +@pytest.fixture(scope='function', autouse=True) +def session(db, app): # pylint: disable=redefined-outer-name, invalid-name """Return a function-scoped session.""" with app.app_context(): - conn = db.engine.connect() - txn = conn.begin() - - options = dict(bind=conn, binds={}) - sess = db.create_scoped_session(options=options) - - # establish a SAVEPOINT just before beginning the test - # (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) - sess.begin_nested() + with db.engine.connect() as conn: + transaction = conn.begin() + sess = db._make_scoped_session(dict(bind=conn)) # pylint: disable=protected-access + # Establish SAVEPOINT (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) + nested = sess.begin_nested() + db.session = sess + db.session.commit = nested.commit + db.session.rollback = nested.rollback + + @event.listens_for(sess, 'after_transaction_end') + def restart_savepoint(sess2, trans): # pylint: disable=unused-variable + nonlocal nested + if trans.nested: + # Handle where test DOESN'T session.commit() + sess2.expire_all() + nested = sess.begin_nested() + # When using a SAVEPOINT via the Session.begin_nested() or Connection.begin_nested() methods, + # the transaction object returned must be used to commit or rollback the SAVEPOINT. + # Calling the Session.commit() or Connection.commit() methods will always commit the + # outermost transaction; this is a SQLAlchemy 2.0 specific behavior that is + # reversed from the 1.x series + db.session = sess + db.session.commit = nested.commit + db.session.rollback = nested.rollback - @event.listens_for(sess(), 'after_transaction_end') - def restart_savepoint(sess2, trans): # pylint: disable=unused-variable - # Detecting whether this is indeed the nested transaction of the test - if trans.nested and not trans._parent.nested: # pylint: disable=protected-access - # Handle where test DOESN'T session.commit(), - sess2.expire_all() - sess.begin_nested() - - db.session = sess - - sql = text('select 1') - sess.execute(sql) - - yield sess - - # Cleanup - sess.remove() - # This instruction rollsback any commit that were executed in the tests. - txn.rollback() - conn.close() + try: + yield db.session + finally: + db.session.remove() + transaction.rollback() @pytest.fixture(scope='session', autouse=True) @@ -176,7 +147,6 @@ def auto(docker_services, app): @pytest.fixture(scope='session') def docker_compose_files(pytestconfig): """Get the docker-compose.yml absolute path.""" - import os return [ os.path.join(str(pytestconfig.rootdir), 'tests/docker', 'docker-compose.yml') ] diff --git a/jobs/payment-jobs/tests/jobs/factory.py b/jobs/payment-jobs/tests/jobs/factory.py index c91a69a30..e665c8d4e 100644 --- a/jobs/payment-jobs/tests/jobs/factory.py +++ b/jobs/payment-jobs/tests/jobs/factory.py @@ -20,11 +20,12 @@ from datetime import datetime, timedelta from pay_api.models import ( - CfsAccount, DistributionCode, DistributionCodeLink, EFTShortnames, Invoice, InvoiceReference, Payment, - PaymentAccount, PaymentLineItem, Receipt, Refund, RoutingSlip, StatementRecipients, StatementSettings) + CfsAccount, DistributionCode, DistributionCodeLink, EFTCredit, EFTFile, EFTShortnameLinks, EFTShortnames, + EFTTransaction, Invoice, InvoiceReference, Payment, PaymentAccount, PaymentLineItem, Receipt, Refund, + RefundsPartial, RoutingSlip, StatementRecipients, StatementSettings) from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, PaymentMethod, PaymentStatus, - PaymentSystem, RoutingSlipStatus) + CfsAccountStatus, EFTProcessStatus, EFTShortnameStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, + PaymentMethod, PaymentStatus, PaymentSystem, RoutingSlipStatus) def factory_premium_payment_account(bcol_user_id='PB25020', bcol_account_id='1234567890', auth_account_id='1234'): @@ -64,7 +65,9 @@ def factory_payment( payment_system_code: str = 'PAYBC', payment_method_code: str = 'CC', payment_status_code: str = PaymentStatus.CREATED.value, payment_date: datetime = datetime.now(), - invoice_number: str = None + invoice_number: str = None, + payment_account_id: int = None, + invoice_amount: float = None, ): """Return Factory.""" return Payment( @@ -72,7 +75,9 @@ def factory_payment( payment_method_code=payment_method_code, payment_status_code=payment_status_code, payment_date=payment_date, - invoice_number=invoice_number + invoice_number=invoice_number, + payment_account_id=payment_account_id, + invoice_amount=invoice_amount ).save() @@ -213,22 +218,70 @@ def factory_routing_slip_account( def factory_create_eft_account(auth_account_id='1234', status=CfsAccountStatus.PENDING.value): """Return Factory.""" - account = PaymentAccount(auth_account_id=auth_account_id, - payment_method=PaymentMethod.EFT.value, - name=f'Test {auth_account_id}').save() - CfsAccount(status=status, account_id=account.id).save() - return account + payment_account = PaymentAccount(auth_account_id=auth_account_id, + payment_method=PaymentMethod.EFT.value, + name=f'Test {auth_account_id}').save() + CfsAccount(status=status, account_id=payment_account.id).save() + return payment_account -def factory_create_eft_shortname(auth_account_id: str, short_name: str): +def factory_create_eft_shortname(short_name: str): """Return Factory.""" short_name = EFTShortnames( - auth_account_id=auth_account_id, short_name=short_name ).save() return short_name +def factory_eft_shortname_link(short_name_id: int, auth_account_id: str = '1234', + updated_by: str = None, updated_on: datetime = datetime.now(), + status_code: str = EFTShortnameStatus.LINKED.value): + """Return an EFT short name link model.""" + return EFTShortnameLinks( + eft_short_name_id=short_name_id, + auth_account_id=auth_account_id, + status_code=status_code, + updated_by=updated_by, + updated_by_name=updated_by, + updated_on=updated_on + ) + + +def factory_create_eft_credit(amount=100, remaining_amount=0, eft_file_id=1, short_name_id=1, payment_account_id=1, + eft_transaction_id=1): + """Return Factory.""" + eft_credit = EFTCredit( + amount=amount, + remaining_amount=remaining_amount, + eft_file_id=eft_file_id, + short_name_id=short_name_id, + payment_account_id=payment_account_id, + eft_transaction_id=eft_transaction_id + ).save() + return eft_credit + + +def factory_create_eft_file(file_ref='test.txt', status_code=EFTProcessStatus.COMPLETED.value): + """Return Factory.""" + eft_file = EFTFile( + file_ref=file_ref, + status_code=status_code + ).save() + return eft_file + + +def factory_create_eft_transaction(file_id=1, line_number=1, line_type='T', + status_code=EFTProcessStatus.COMPLETED.value): + """Return Factory.""" + eft_transaction = EFTTransaction( + file_id=file_id, + line_number=line_number, + line_type=line_type, + status_code=status_code, + ).save() + return eft_transaction + + def factory_create_account(auth_account_id: str = '1234', payment_method_code: str = PaymentMethod.DIRECT_PAY.value, status: str = CfsAccountStatus.PENDING.value, statement_notification_enabled: bool = True): """Return payment account model.""" @@ -334,3 +387,20 @@ def factory_refund_invoice( requested_by='TEST', details=details ).save() + + +def factory_refund_partial( + payment_line_item_id: int, + refund_amount: float, + refund_type: str, + created_by='test', + created_on: datetime = datetime.now() +): + """Return Factory.""" + return RefundsPartial( + payment_line_item_id=payment_line_item_id, + refund_amount=refund_amount, + refund_type=refund_type, + created_by=created_by, + created_on=created_on + ).save() diff --git a/jobs/payment-jobs/tests/jobs/test_cfs_create_account_task.py b/jobs/payment-jobs/tests/jobs/test_cfs_create_account_task.py index e27435880..6c551f1ec 100644 --- a/jobs/payment-jobs/tests/jobs/test_cfs_create_account_task.py +++ b/jobs/payment-jobs/tests/jobs/test_cfs_create_account_task.py @@ -28,7 +28,7 @@ from tasks.cfs_create_account_task import CreateAccountTask from utils import mailer -from .factory import factory_create_online_banking_account, factory_create_pad_account +from .factory import factory_create_eft_account, factory_create_online_banking_account, factory_create_pad_account def test_create_account_setup(session): @@ -52,6 +52,17 @@ def test_create_pad_account(session): assert cfs_account.payment_instrument_number +def test_create_eft_account(session): + """Test create account.""" + # Create a pending account first, then call the job + account = factory_create_eft_account(auth_account_id='1') + CreateAccountTask.create_accounts() + account = PaymentAccount.find_by_id(account.id) + cfs_account: CfsAccount = CfsAccount.find_effective_by_account_id(account.id) + assert cfs_account.status == CfsAccountStatus.ACTIVE.value + assert cfs_account.payment_instrument_number is None + + def test_create_pad_account_user_error(session): """Test create account.""" # Create a pending account first, then call the job diff --git a/jobs/payment-jobs/tests/jobs/test_cfs_create_invoice_task.py b/jobs/payment-jobs/tests/jobs/test_cfs_create_invoice_task.py index 49c35e296..2df2a00d5 100644 --- a/jobs/payment-jobs/tests/jobs/test_cfs_create_invoice_task.py +++ b/jobs/payment-jobs/tests/jobs/test_cfs_create_invoice_task.py @@ -16,7 +16,7 @@ Test-Suite to ensure that the CreateInvoiceTask is working as expected. """ -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from unittest.mock import patch from pay_api.models import DistributionCode as DistributionCodeModel @@ -32,8 +32,8 @@ from tasks.cfs_create_invoice_task import CreateInvoiceTask from .factory import ( - factory_create_online_banking_account, factory_create_pad_account, factory_invoice, factory_payment_line_item, - factory_routing_slip_account) + factory_create_eft_account, factory_create_online_banking_account, factory_create_pad_account, factory_invoice, + factory_payment_line_item, factory_routing_slip_account) def test_create_invoice(session): @@ -272,3 +272,128 @@ def test_create_online_banking_transaction(session): assert inv_ref assert updated_invoice.invoice_status_code == InvoiceStatus.SETTLEMENT_SCHEDULED.value + + +def test_create_eft_invoice(session): + """Assert EFT invoice is created.""" + account = factory_create_eft_account(auth_account_id='1', status=CfsAccountStatus.ACTIVE.value) + previous_day = datetime.now(tz=timezone.utc) - timedelta(days=1) + # Create an invoice for this account + invoice = factory_invoice(payment_account=account, created_on=previous_day, total=10, + status_code=InvoiceStatus.APPROVED.value, payment_method_code=PaymentMethod.EFT.value) + + fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTANN') + line = factory_payment_line_item(invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id) + line.save() + assert invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + CreateInvoiceTask.create_invoices() + + updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) + invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel. \ + find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) + + assert invoice_reference + assert updated_invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + +def test_create_eft_invoice_rerun(session): + """Assert EFT invoice is created.""" + account = factory_create_eft_account(auth_account_id='1', status=CfsAccountStatus.ACTIVE.value) + previous_day = datetime.now(tz=timezone.utc) - timedelta(days=1) + # Create an invoice for this account + invoice = factory_invoice(payment_account=account, created_on=previous_day, total=10, + status_code=InvoiceStatus.APPROVED.value, payment_method_code=PaymentMethod.EFT.value) + + fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTANN') + line = factory_payment_line_item(invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id) + line.save() + invoice_response = {'invoice_number': '10021', 'pbc_ref_number': '10005', 'party_number': '11111', + 'party_name': 'invoice'} + assert invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + with patch.object(CFSService, 'create_account_invoice', return_value=invoice_response) as mock_cfs: + CreateInvoiceTask.create_invoices() + mock_cfs.assert_called() + + updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) + inv_ref: InvoiceReferenceModel = InvoiceReferenceModel. \ + find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) + + assert inv_ref + assert updated_invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + with patch.object(CFSService, 'create_account_invoice', return_value=invoice_response) as mock_cfs: + CreateInvoiceTask.create_invoices() + mock_cfs.assert_not_called() + + +def test_create_eft_invoice_on_frozen_account(session): + """Assert EFT invoice is created.""" + account = factory_create_eft_account(auth_account_id='1', status=CfsAccountStatus.FREEZE.value) + previous_day = datetime.now(tz=timezone.utc) - timedelta(days=1) + # Create an invoice for this account + invoice = factory_invoice(payment_account=account, created_on=previous_day, total=10, + status_code=InvoiceStatus.APPROVED.value, payment_method_code=PaymentMethod.EFT.value) + + fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTANN') + line = factory_payment_line_item(invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id) + line.save() + + assert invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + CreateInvoiceTask.create_invoices() + + updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) + inv_ref: InvoiceReferenceModel = InvoiceReferenceModel. \ + find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) + + assert inv_ref is None + assert updated_invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + +def test_create_eft_invoices(session): + """Assert EFT invoices are created.""" + account = factory_create_eft_account(auth_account_id='1', status=CfsAccountStatus.ACTIVE.value) + previous_day = datetime.now(tz=timezone.utc) - timedelta(days=1) + # Create an invoice for this account + invoice = factory_invoice(payment_account=account, created_on=previous_day, total=10, + status_code=InvoiceStatus.APPROVED.value, payment_method_code=PaymentMethod.EFT.value) + fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTANN') + line = factory_payment_line_item(invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id) + line.save() + + # Create another invoice for this account + invoice2 = factory_invoice(payment_account=account, created_on=previous_day, total=10, + status_code=InvoiceStatus.APPROVED.value, payment_method_code=PaymentMethod.EFT.value) + fee_schedule2 = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTADD') + line2 = factory_payment_line_item(invoice2.id, fee_schedule_id=fee_schedule2.fee_schedule_id) + line2.save() + + CreateInvoiceTask.create_invoices() + invoice2 = InvoiceModel.find_by_id(invoice2.id) + invoice = InvoiceModel.find_by_id(invoice.id) + assert invoice2.invoice_status_code == invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + +def test_create_eft_invoice_before_cutoff(session): + """Assert EFT invoices are created.""" + account = factory_create_eft_account(auth_account_id='1', status=CfsAccountStatus.ACTIVE.value) + previous_day = datetime.now(tz=timezone.utc) - timedelta(days=2) + # Create an invoice for this account + invoice = factory_invoice(payment_account=account, created_on=previous_day, total=10, + status_code=InvoiceStatus.APPROVED.value, payment_method_code=PaymentMethod.EFT.value) + fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTANN') + line = factory_payment_line_item(invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id) + line.save() + + assert invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + CreateInvoiceTask.create_invoices() + + updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) + inv_ref: InvoiceReferenceModel = InvoiceReferenceModel. \ + find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) + + assert inv_ref is not None # As EFT will be summed up for all outstanding invoices + assert updated_invoice.invoice_status_code == InvoiceStatus.APPROVED.value diff --git a/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py b/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py deleted file mode 100644 index 09a3d6254..000000000 --- a/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py +++ /dev/null @@ -1,178 +0,0 @@ -# Copyright © 2023 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tests to assure the EFT CGI Transfer Job. - -Test-Suite to ensure that the EFT Transfer task is working as expected. -""" -from datetime import datetime -from typing import List - -import pytest -from flask import Flask -from pay_api.models import DistributionCode, EFTGLTransfer, EjvFile, EjvHeader, EjvInvoiceLink, FeeSchedule, Invoice, db -from pay_api.utils.enums import DisbursementStatus, EFTGlTransferType, EjvFileType, InvoiceStatus, PaymentMethod - -import config -from tasks.eft_transfer_task import EftTransferTask - -from .factory import ( - factory_create_eft_account, factory_create_eft_shortname, factory_distribution, factory_invoice, - factory_payment_line_item) - - -app = None - - -@pytest.fixture -def setup(): - """Initialize app with test env for testing.""" - global app - app = Flask(__name__) - app.env = 'testing' - app.config.from_object(config.CONFIGURATION['testing']) - app.config['EFT_HOLDING_GL'] = '1128888888888888888000000000000000' - - -def test_eft_transfer(setup, session, monkeypatch): - """Test EFT Holdings GL Transfer for EFT invoices. - - Steps: - 1) Create GL codes to match GA batch type. - 2) Create account to short name mappings - 3) Create paid invoices for EFT. - 4) Run the job and assert results. - """ - monkeypatch.setattr('pysftp.Connection.put', lambda *args, **kwargs: None) - - corp_type = 'BEN' - filing_type = 'BCINC' - - # Find fee schedule which have service fees. - fee_schedule: FeeSchedule = FeeSchedule.find_by_filing_type_and_corp_type(corp_type, filing_type) - # Create a service fee distribution code - service_fee_dist_code = factory_distribution(name='service fee', client='112', reps_centre='99999', - service_line='99999', - stob='9999', project_code='9999999') - service_fee_dist_code.save() - - dist_code: DistributionCode = DistributionCode.find_by_active_for_fee_schedule(fee_schedule.fee_schedule_id) - # Update fee dist code to match the requirement. - dist_code.client = '112' - dist_code.responsibility_centre = '11111' - dist_code.service_line = '22222' - dist_code.stob = '3333' - dist_code.project_code = '4444444' - dist_code.service_fee_distribution_code_id = service_fee_dist_code.distribution_code_id - dist_code.save() - - eft_holding_gl = app.config['EFT_HOLDING_GL'] - distribution_gl = EftTransferTask.get_distribution_string(dist_code).strip() - service_fee_gl = EftTransferTask.get_distribution_string(service_fee_dist_code).strip() - - # GA - eft_account_1 = factory_create_eft_account(auth_account_id='1') - eft_shortname_1 = factory_create_eft_shortname(auth_account_id='1', short_name='SHORTNAME1') - eft_account_2 = factory_create_eft_account(auth_account_id='2') - eft_shortname_2 = factory_create_eft_shortname(auth_account_id='2', short_name='SHORTNAME2') - - eft_accounts = [eft_account_1, eft_account_2] - invoices: List[Invoice] = [] - for account in eft_accounts: - inv = factory_invoice(payment_account=account, corp_type_code=corp_type, total=101.5, - status_code=InvoiceStatus.PAID.value, payment_method_code=PaymentMethod.EFT.value) - factory_payment_line_item(invoice_id=inv.id, - fee_schedule_id=fee_schedule.fee_schedule_id, - filing_fees=100, - total=100, - service_fees=1.5, - fee_dist_id=dist_code.distribution_code_id) - invoices.append(inv) - - with app.app_context(): - EftTransferTask.create_ejv_file() - - # Lookup invoice and assert disbursement status - for invoice in invoices: - ejv_inv_link: EjvInvoiceLink = db.session.query(EjvInvoiceLink) \ - .filter(EjvInvoiceLink.invoice_id == invoice.id).first() - assert ejv_inv_link - - ejv_header = db.session.query(EjvHeader).filter(EjvHeader.id == ejv_inv_link.ejv_header_id).first() - assert ejv_header.disbursement_status_code == DisbursementStatus.UPLOADED.value - assert ejv_header - - ejv_file: EjvFile = EjvFile.find_by_id(ejv_header.ejv_file_id) - assert ejv_file - assert ejv_file.disbursement_status_code == DisbursementStatus.UPLOADED.value - assert ejv_file.file_type == EjvFileType.TRANSFER.value - - eft_transfers: List[EFTGLTransfer] = db.session.query(EFTGLTransfer).all() - - now = datetime.now().date() - - assert eft_transfers - assert len(eft_transfers) == 4 - - # Assert first short name line item distribution - assert eft_transfers[0].id is not None - assert eft_transfers[0].short_name_id == eft_shortname_1.id - assert eft_transfers[0].invoice_id == invoices[0].id - assert eft_transfers[0].transfer_amount == invoices[0].payment_line_items[0].total - assert eft_transfers[0].transfer_type == EFTGlTransferType.TRANSFER.value - assert eft_transfers[0].transfer_date.date() == now - assert eft_transfers[0].is_processed - assert eft_transfers[0].processed_on.date() == now - assert eft_transfers[0].created_on.date() == now - assert eft_transfers[0].source_gl == eft_holding_gl - assert eft_transfers[0].target_gl == distribution_gl - - # Assert first short name service fee distribution - assert eft_transfers[1].id is not None - assert eft_transfers[1].short_name_id == eft_shortname_1.id - assert eft_transfers[1].invoice_id == invoices[0].id - assert eft_transfers[1].transfer_type == EFTGlTransferType.TRANSFER.value - assert eft_transfers[1].transfer_amount == invoices[0].payment_line_items[0].service_fees - assert eft_transfers[1].transfer_date.date() == now - assert eft_transfers[1].is_processed - assert eft_transfers[1].processed_on.date() == now - assert eft_transfers[1].created_on.date() == now - assert eft_transfers[1].source_gl == eft_holding_gl - assert eft_transfers[1].target_gl == service_fee_gl - - # Assert second short name line item distribution - assert eft_transfers[2].id is not None - assert eft_transfers[2].short_name_id == eft_shortname_2.id - assert eft_transfers[2].invoice_id == invoices[1].id - assert eft_transfers[2].transfer_type == EFTGlTransferType.TRANSFER.value - assert eft_transfers[2].transfer_amount == invoices[1].payment_line_items[0].total - assert eft_transfers[2].transfer_date.date() == now - assert eft_transfers[2].is_processed - assert eft_transfers[2].processed_on.date() == now - assert eft_transfers[2].created_on.date() == now - assert eft_transfers[2].source_gl == eft_holding_gl - assert eft_transfers[2].target_gl == distribution_gl - - # Assert second short name service fee distribution - assert eft_transfers[3].id is not None - assert eft_transfers[3].short_name_id == eft_shortname_2.id - assert eft_transfers[3].invoice_id == invoices[1].id - assert eft_transfers[3].transfer_type == EFTGlTransferType.TRANSFER.value - assert eft_transfers[3].transfer_amount == invoices[1].payment_line_items[0].service_fees - assert eft_transfers[3].transfer_date.date() == now - assert eft_transfers[3].is_processed - assert eft_transfers[3].processed_on.date() == now - assert eft_transfers[3].created_on.date() == now - assert eft_transfers[3].source_gl == eft_holding_gl - assert eft_transfers[3].target_gl == service_fee_gl diff --git a/jobs/payment-jobs/tests/jobs/test_ejv_partner_distribution_task.py b/jobs/payment-jobs/tests/jobs/test_ejv_partner_distribution_task.py index 7dac7cd3a..8d1191db6 100644 --- a/jobs/payment-jobs/tests/jobs/test_ejv_partner_distribution_task.py +++ b/jobs/payment-jobs/tests/jobs/test_ejv_partner_distribution_task.py @@ -22,7 +22,7 @@ from flask import current_app from freezegun import freeze_time from pay_api.models import CorpType as CorpTypeModel -from pay_api.models import DistributionCode, EjvFile, EjvHeader, EjvInvoiceLink, FeeSchedule, Invoice, db +from pay_api.models import DistributionCode, EjvFile, EjvHeader, EjvLink, FeeSchedule, Invoice, db from pay_api.utils.enums import CfsAccountStatus, DisbursementStatus, InvoiceStatus, PaymentMethod from tasks.ejv_partner_distribution_task import EjvPartnerDistributionTask @@ -89,7 +89,7 @@ def test_disbursement_for_partners(session, monkeypatch, client_code, batch_type invoice = Invoice.find_by_id(invoice.id) assert invoice.disbursement_status_code == DisbursementStatus.UPLOADED.value - ejv_inv_link = db.session.query(EjvInvoiceLink).filter(EjvInvoiceLink.invoice_id == invoice.id).first() + ejv_inv_link = db.session.query(EjvLink).filter(EjvLink.link_id == invoice.id).first() assert ejv_inv_link ejv_header = db.session.query(EjvHeader).filter(EjvHeader.id == ejv_inv_link.ejv_header_id).first() diff --git a/jobs/payment-jobs/tests/jobs/test_ejv_partner_partial_refund_distribution_task.py b/jobs/payment-jobs/tests/jobs/test_ejv_partner_partial_refund_distribution_task.py new file mode 100644 index 000000000..7ab4cce30 --- /dev/null +++ b/jobs/payment-jobs/tests/jobs/test_ejv_partner_partial_refund_distribution_task.py @@ -0,0 +1,86 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests to assure the CGI EJV Job. + +Test-Suite to ensure that the CgiEjvJob is working as expected. +""" +from datetime import datetime, timedelta + +from flask import current_app +from freezegun import freeze_time +from pay_api.models import CorpType as CorpTypeModel +from pay_api.models import DistributionCode, EjvFile, EjvHeader, EjvLink, FeeSchedule, db +from pay_api.utils.enums import DisbursementStatus, RefundsPartialType + +from tasks.ejv_partner_distribution_task import EjvPartnerDistributionTask + +from .factory import ( + factory_create_direct_pay_account, factory_distribution, factory_distribution_link, factory_invoice, + factory_invoice_reference, factory_payment, factory_payment_line_item, factory_refund_partial) + + +def test_partial_refund_disbursement(session, monkeypatch): + """Test partial refund disbursement.""" + monkeypatch.setattr('pysftp.Connection.put', lambda *args, **kwargs: None) + corp_type: CorpTypeModel = CorpTypeModel.find_by_code('VS') + + pay_account = factory_create_direct_pay_account() + + disbursement_distribution: DistributionCode = factory_distribution(name='VS Disbursement', client='112') + service_fee_distribution: DistributionCode = factory_distribution(name='VS Service Fee', client='112') + fee_distribution: DistributionCode = factory_distribution( + name='VS Fee distribution', client='112', service_fee_dist_id=service_fee_distribution.distribution_code_id, + disbursement_dist_id=disbursement_distribution.distribution_code_id + ) + fee_schedule: FeeSchedule = FeeSchedule.find_by_filing_type_and_corp_type(corp_type.code, 'WILLNOTICE') + factory_distribution_link(fee_distribution.distribution_code_id, fee_schedule.fee_schedule_id) + + invoice = factory_invoice(payment_account=pay_account, disbursement_status_code=DisbursementStatus.COMPLETED.value, + corp_type_code=corp_type.code, total=21.5, status_code='PAID') + pli = factory_payment_line_item(invoice_id=invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id, + filing_fees=0, total=1.5, service_fees=1.5, + fee_dist_id=fee_distribution.distribution_code_id) + + inv_ref = factory_invoice_reference(invoice_id=invoice.id) + factory_payment(invoice_number=inv_ref.invoice_number, payment_status_code='COMPLETED') + + refund_partial = factory_refund_partial(pli.id, refund_amount=1.5, created_by='test', + refund_type=RefundsPartialType.SERVICE_FEES.value) + + assert refund_partial.disbursement_status_code is None + # Lookup refund_partial_link + refund_partial_link = EjvLink.find_ejv_link_by_link_id(refund_partial.id) + assert refund_partial_link is None + + day_after_time_delay = datetime.today() + timedelta(days=( + current_app.config.get('DISBURSEMENT_DELAY_IN_DAYS') + 1)) + + with freeze_time(day_after_time_delay): + EjvPartnerDistributionTask.create_ejv_file() + + ejv_link = db.session.query(EjvLink).filter(EjvLink.link_id == refund_partial.id).first() + assert ejv_link + + ejv_header = db.session.query(EjvHeader).filter(EjvHeader.id == ejv_link.ejv_header_id).first() + assert ejv_header.disbursement_status_code == DisbursementStatus.UPLOADED.value + assert ejv_header + + ejv_file = EjvFile.find_by_id(ejv_header.ejv_file_id) + assert ejv_file + assert ejv_file.disbursement_status_code == DisbursementStatus.UPLOADED.value + + refund_partial_link = EjvLink.find_ejv_link_by_link_id(refund_partial.id) + assert refund_partial_link.disbursement_status_code == DisbursementStatus.UPLOADED.value + assert refund_partial.disbursement_status_code == DisbursementStatus.UPLOADED.value diff --git a/jobs/payment-jobs/tests/jobs/test_ejv_payment_task.py b/jobs/payment-jobs/tests/jobs/test_ejv_payment_task.py index 5eae96032..9c1a67363 100644 --- a/jobs/payment-jobs/tests/jobs/test_ejv_payment_task.py +++ b/jobs/payment-jobs/tests/jobs/test_ejv_payment_task.py @@ -16,8 +16,7 @@ Test-Suite to ensure that the CgiEjvJob is working as expected. """ -from pay_api.models import ( - DistributionCode, EjvFile, EjvHeader, EjvInvoiceLink, FeeSchedule, Invoice, InvoiceReference, db) +from pay_api.models import DistributionCode, EjvFile, EjvHeader, EjvLink, FeeSchedule, Invoice, InvoiceReference, db from pay_api.utils.enums import DisbursementStatus, EjvFileType, InvoiceReferenceStatus, InvoiceStatus from tasks.ejv_payment_task import EjvPaymentTask @@ -87,8 +86,8 @@ def test_payments_for_gov_accounts(session, monkeypatch): InvoiceReferenceStatus.ACTIVE.value) assert invoice_ref - ejv_inv_link: EjvInvoiceLink = db.session.query(EjvInvoiceLink)\ - .filter(EjvInvoiceLink.invoice_id == inv_id).first() + ejv_inv_link: EjvLink = db.session.query(EjvLink)\ + .filter(EjvLink.link_id == inv_id).first() assert ejv_inv_link ejv_header = db.session.query(EjvHeader).filter(EjvHeader.id == ejv_inv_link.ejv_header_id).first() @@ -127,8 +126,8 @@ def test_payments_for_gov_accounts(session, monkeypatch): InvoiceReferenceStatus.ACTIVE.value) assert invoice_ref - ejv_inv_link = db.session.query(EjvInvoiceLink).filter(EjvInvoiceLink.invoice_id == inv_id)\ - .filter(EjvInvoiceLink.disbursement_status_code == DisbursementStatus.UPLOADED.value).first() + ejv_inv_link = db.session.query(EjvLink).filter(EjvLink.link_id == inv_id)\ + .filter(EjvLink.disbursement_status_code == DisbursementStatus.UPLOADED.value).first() assert ejv_inv_link ejv_header = db.session.query(EjvHeader).filter(EjvHeader.id == ejv_inv_link.ejv_header_id).first() diff --git a/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py b/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py new file mode 100644 index 000000000..0c4cd4942 --- /dev/null +++ b/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py @@ -0,0 +1,131 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests to assure the CreateAccountTask. + +Test-Suite to ensure that the CreateAccountTask for electronic funds transfer is working as expected. +""" +from datetime import datetime +from unittest.mock import patch + +from pay_api.models import CfsAccount as CfsAccountModel +from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel +from pay_api.models import EFTShortnames as EFTShortnameModel +from pay_api.models import FeeSchedule as FeeScheduleModel +from pay_api.models import PaymentAccount as PaymentAccountModel +from pay_api.utils.enums import ( + CfsAccountStatus, EFTShortnameStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod) +from pay_api.services import CFSService +from tasks.electronic_funds_transfer_task import ElectronicFundsTransferTask + +from .factory import ( + factory_create_eft_account, factory_create_eft_credit, factory_create_eft_file, factory_create_eft_shortname, + factory_create_eft_transaction, factory_eft_shortname_link, factory_invoice, factory_invoice_reference, + factory_payment, factory_payment_line_item, factory_receipt) + + +def test_link_electronic_funds_transfers(session): + """Test link electronic funds transfers.""" + auth_account_id = '1234' + short_name = 'TEST1' + + payment_account = factory_create_eft_account(auth_account_id=auth_account_id, status=CfsAccountStatus.ACTIVE.value) + eft_file = factory_create_eft_file() + factory_create_eft_transaction(file_id=eft_file.id) + eft_short_name = factory_create_eft_shortname(short_name=short_name) + eft_short_name_link = factory_eft_shortname_link( + short_name_id=eft_short_name.id, + auth_account_id=auth_account_id, + updated_by='test' + ).save() + invoice = factory_invoice(payment_account=payment_account, payment_method_code=PaymentMethod.EFT.value) + factory_invoice_reference(invoice_id=invoice.id) + factory_payment(payment_account_id=payment_account.id, payment_method_code=PaymentMethod.EFT.value, + invoice_amount=351.50) + factory_create_eft_credit( + amount=100, remaining_amount=0, eft_file_id=eft_file.id, short_name_id=eft_short_name.id, + payment_account_id=payment_account.id) + + eft_short_name = EFTShortnameModel.find_by_short_name(short_name) + eft_short_name_link = EFTShortnameLinksModel.find_by_short_name_id(eft_short_name.id)[0] + eft_short_name_link.updated_by = 'test' + eft_short_name_link.updated_by_name = 'test' + eft_short_name_link.updated_on = datetime.now() + eft_short_name.save() + + payment_account: PaymentAccountModel = PaymentAccountModel.find_by_auth_account_id( + eft_short_name_link.auth_account_id) + + cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id( + payment_account.id) + + with patch('pay_api.services.CFSService.create_cfs_receipt') as mock_create_cfs: + with patch.object(CFSService, 'get_receipt') as mock_get_receipt: + ElectronicFundsTransferTask.link_electronic_funds_transfers() + mock_create_cfs.assert_called() + mock_get_receipt.assert_called() + + cfs_account: CfsAccountModel = CfsAccountModel.find_by_id(cfs_account.id) + assert cfs_account.status == CfsAccountStatus.ACTIVE.value + + +def test_unlink_electronic_funds_transfers(session): + """Test unlink electronic funds transfers.""" + auth_account_id = '1234' + short_name = 'TEST1' + receipt_number = '1111R' + invoice_number = '1234' + + payment_account = factory_create_eft_account(auth_account_id=auth_account_id, status=CfsAccountStatus.ACTIVE.value) + eft_file = factory_create_eft_file() + eft_transaction = factory_create_eft_transaction(file_id=eft_file.id) + eft_short_name = factory_create_eft_shortname(short_name=short_name) + eft_short_name_link = factory_eft_shortname_link( + short_name_id=eft_short_name.id, + auth_account_id=auth_account_id, + updated_by='test', + status_code=EFTShortnameStatus.UNLINKED.value + ).save() + + invoice = factory_invoice(payment_account=payment_account, total=30, + status_code=InvoiceStatus.PAID.value, + payment_method_code=PaymentMethod.EFT.value) + + factory_payment(payment_account_id=payment_account.id, payment_method_code=PaymentMethod.EFT.value, + invoice_amount=351.50, invoice_number=invoice_number) + fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTANN') + factory_payment_line_item(invoice_id=invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id) + factory_invoice_reference(invoice_id=invoice.id, status_code=InvoiceReferenceStatus.COMPLETED.value, + invoice_number=invoice_number) + factory_create_eft_credit( + amount=100, remaining_amount=0, eft_file_id=eft_file.id, short_name_id=eft_short_name.id, + payment_account_id=payment_account.id, + eft_transaction_id=eft_transaction.id) + + factory_receipt(invoice.id, receipt_number) + + eft_short_name = EFTShortnameModel.find_by_short_name(short_name) + eft_short_name_link = EFTShortnameLinksModel.find_by_short_name_id(eft_short_name.id)[0] + eft_short_name_link.updated_by = None + eft_short_name_link.updated_by_name = None + eft_short_name_link.updated_on = None + eft_short_name.save() + + session.commit() + + with patch('pay_api.services.CFSService.reverse_rs_receipt_in_cfs') as mock_reverse: + with patch('pay_api.services.CFSService.create_cfs_receipt') as mock_create_receipt: + ElectronicFundsTransferTask.unlink_electronic_funds_transfers() + mock_reverse.assert_called() + mock_create_receipt.assert_called() diff --git a/jobs/payment-jobs/tests/jobs/test_generate_statements.py b/jobs/payment-jobs/tests/jobs/test_generate_statements.py index df9faeaa5..5f31a016b 100644 --- a/jobs/payment-jobs/tests/jobs/test_generate_statements.py +++ b/jobs/payment-jobs/tests/jobs/test_generate_statements.py @@ -16,7 +16,7 @@ Test-Suite to ensure that the UpdateStalePayment is working as expected. """ -from datetime import datetime, timedelta, timezone +from datetime import datetime, timedelta import pytz from freezegun import freeze_time @@ -133,7 +133,6 @@ def test_bcol_weekly_to_eft_statement(session): total=50) assert weekly_invoice is not None - assert weekly_invoice.created_on == invoice_create_date.astimezone(timezone.utc).replace(tzinfo=None) statement_from_date = localize_date(datetime(2023, 10, 8, 12, 0)) statement_to_date = localize_date(datetime(2023, 10, 12, 12, 0)) @@ -179,7 +178,6 @@ def test_bcol_weekly_to_eft_statement(session): total=50) assert monthly_invoice is not None - assert monthly_invoice.created_on == invoice_create_date.astimezone(timezone.utc).replace(tzinfo=None) # Regenerate monthly statement using date override - it will clean up the previous empty monthly statement first StatementTask.generate_statements((generate_date - timedelta(days=1)).strftime('%Y-%m-%d')) @@ -212,7 +210,6 @@ def test_bcol_monthly_to_eft_statement(session): total=50) assert bcol_invoice is not None - assert bcol_invoice.created_on == invoice_create_date.astimezone(timezone.utc).replace(tzinfo=None) statement_from_date = localize_date(datetime(2023, 10, 1, 12, 0)) statement_to_date = localize_date(datetime(2023, 10, 30, 12, 0)) @@ -259,7 +256,6 @@ def test_bcol_monthly_to_eft_statement(session): total=50) assert monthly_invoice is not None - assert monthly_invoice.created_on == invoice_create_date.astimezone(timezone.utc).replace(tzinfo=None) # Regenerate monthly statement using date override - it will clean up the previous empty monthly statement first StatementTask.generate_statements((generate_date - timedelta(days=1)).strftime('%Y-%m-%d')) diff --git a/jobs/payment-jobs/tests/jobs/test_statement_due_task.py b/jobs/payment-jobs/tests/jobs/test_statement_due_task.py index 062208c95..7b4fd3960 100644 --- a/jobs/payment-jobs/tests/jobs/test_statement_due_task.py +++ b/jobs/payment-jobs/tests/jobs/test_statement_due_task.py @@ -114,39 +114,37 @@ def test_send_unpaid_statement_notification(setup, session): summary = Statement.get_summary(account.auth_account_id, statements[0][0].id) total_amount_owing = summary['total_due'] - with app.app_context(): - # Assert notification was published to the mailer queue - with patch('tasks.statement_due_task.publish_payment_notification') as mock_mailer: - # Freeze time to due date - trigger due notification - with freeze_time(last_day): - StatementDueTask.process_unpaid_statements() - mock_mailer.assert_called_with(StatementNotificationInfo(auth_account_id=account.auth_account_id, - statement=statements[0][0], - is_due=True, - due_date=last_day.date(), - emails=statement_recipient.email, - total_amount_owing=total_amount_owing)) - - # Freeze time to due date - trigger reminder notification - with freeze_time(last_day - timedelta(days=7)): - StatementDueTask.process_unpaid_statements() - mock_mailer.assert_called_with(StatementNotificationInfo(auth_account_id=account.auth_account_id, - statement=statements[0][0], - is_due=False, - due_date=last_day.date(), - emails=statement_recipient.email, - total_amount_owing=total_amount_owing)) + # Assert notification was published to the mailer queue + with patch('tasks.statement_due_task.publish_payment_notification') as mock_mailer: + # Freeze time to due date - trigger due notification + with freeze_time(last_day): + StatementDueTask.process_unpaid_statements() + mock_mailer.assert_called_with(StatementNotificationInfo(auth_account_id=account.auth_account_id, + statement=statements[0][0], + is_due=True, + due_date=last_day.date(), + emails=statement_recipient.email, + total_amount_owing=total_amount_owing)) + + # Freeze time to due date - trigger reminder notification + with freeze_time(last_day - timedelta(days=7)): + StatementDueTask.process_unpaid_statements() + mock_mailer.assert_called_with(StatementNotificationInfo(auth_account_id=account.auth_account_id, + statement=statements[0][0], + is_due=False, + due_date=last_day.date(), + emails=statement_recipient.email, + total_amount_owing=total_amount_owing)) def test_unpaid_statement_notification_not_sent(setup, session): """Assert payment reminder event is not being sent.""" - with app.app_context(): - # Assert notification was published to the mailer queue - with patch('tasks.statement_due_task.publish_payment_notification') as mock_mailer: - # Freeze time to 10th of the month - should not trigger any notification - with freeze_time(current_local_time().replace(day=10)): - StatementDueTask.process_unpaid_statements() - mock_mailer.assert_not_called() + # Assert notification was published to the mailer queue + with patch('tasks.statement_due_task.publish_payment_notification') as mock_mailer: + # Freeze time to 10th of the month - should not trigger any notification + with freeze_time(current_local_time().replace(day=10)): + StatementDueTask.process_unpaid_statements() + mock_mailer.assert_not_called() def test_overdue_invoices_updated(setup, session): @@ -173,9 +171,8 @@ def test_overdue_invoices_updated(setup, session): assert invoice2.invoice_status_code == InvoiceStatus.CREATED.value assert account.payment_method == PaymentMethod.EFT.value - with app.app_context(): - # Freeze time to 1st of the month - should trigger overdue status update for previous month invoices - with freeze_time(current_local_time().replace(day=1)): - StatementDueTask.process_unpaid_statements() - assert invoice.invoice_status_code == InvoiceStatus.OVERDUE.value - assert invoice2.invoice_status_code == InvoiceStatus.CREATED.value + # Freeze time to 1st of the month - should trigger overdue status update for previous month invoices + with freeze_time(current_local_time().replace(day=1)): + StatementDueTask.process_unpaid_statements() + assert invoice.invoice_status_code == InvoiceStatus.OVERDUE.value + assert invoice2.invoice_status_code == InvoiceStatus.CREATED.value diff --git a/jobs/payment-jobs/tests/jobs/test_statement_notification_task.py b/jobs/payment-jobs/tests/jobs/test_statement_notification_task.py index 4281dafac..750444122 100644 --- a/jobs/payment-jobs/tests/jobs/test_statement_notification_task.py +++ b/jobs/payment-jobs/tests/jobs/test_statement_notification_task.py @@ -91,46 +91,45 @@ def test_send_notifications(session): ]) def test_send_monthly_notifications(setup, session, payment_method_code): # pylint: disable=unused-argument """Test send monthly statement notifications.""" - with app.app_context(): - # create statement, invoice, payment data for previous month - last_month, last_year = get_previous_month_and_year() - previous_month_year = datetime(last_year, last_month, 5) - - account, invoice, inv_ref, payment, \ - statement_recipient, statement_settings = create_test_data(payment_method_code, - previous_month_year, - StatementFrequency.MONTHLY.value) - - assert invoice.payment_method_code == payment_method_code - assert account.payment_method == payment_method_code - - # Generate statement for previous month - freeze time to the 1st of the current month - with freeze_time(datetime.now().replace(day=1)): - StatementTask.generate_statements() - - # Assert statements and invoice was created - statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, - limit=100) - assert statements is not None - assert len(statements) == 2 # items results and page total - assert len(statements[0]) == 1 # items - invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) - assert invoices is not None - assert invoices[0].invoice_id == invoice.id - - # Assert notification send_email was invoked - with patch.object(StatementNotificationTask, 'send_email', return_value=True) as mock_mailer: - with patch('tasks.statement_notification_task.get_token') as mock_get_token: - mock_get_token.return_value = 'mock_token' - StatementNotificationTask.send_notifications() - mock_get_token.assert_called_once() - # Assert token and email recipient - mock any for HTML generated - mock_mailer.assert_called_with(mock_get_token.return_value, statement_recipient.email, ANY) - - # Assert statement notification code indicates success - statement: Statement = Statement.find_by_id(statements[0][0].id) - assert statement is not None - assert statement.notification_status_code == NotificationStatus.SUCCESS.value + # create statement, invoice, payment data for previous month + last_month, last_year = get_previous_month_and_year() + previous_month_year = datetime(last_year, last_month, 5) + + account, invoice, inv_ref, payment, \ + statement_recipient, statement_settings = create_test_data(payment_method_code, + previous_month_year, + StatementFrequency.MONTHLY.value) + + assert invoice.payment_method_code == payment_method_code + assert account.payment_method == payment_method_code + + # Generate statement for previous month - freeze time to the 1st of the current month + with freeze_time(datetime.now().replace(day=1)): + StatementTask.generate_statements() + + # Assert statements and invoice was created + statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, + limit=100) + assert statements is not None + assert len(statements) == 2 # items results and page total + assert len(statements[0]) == 1 # items + invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) + assert invoices is not None + assert invoices[0].invoice_id == invoice.id + + # Assert notification send_email was invoked + with patch.object(StatementNotificationTask, 'send_email', return_value=True) as mock_mailer: + with patch('tasks.statement_notification_task.get_token') as mock_get_token: + mock_get_token.return_value = 'mock_token' + StatementNotificationTask.send_notifications() + mock_get_token.assert_called_once() + # Assert token and email recipient - mock any for HTML generated + mock_mailer.assert_called_with(mock_get_token.return_value, statement_recipient.email, ANY) + + # Assert statement notification code indicates success + statement: Statement = Statement.find_by_id(statements[0][0].id) + assert statement is not None + assert statement.notification_status_code == NotificationStatus.SUCCESS.value @pytest.mark.parametrize('payment_method_code', [ @@ -144,174 +143,170 @@ def test_send_monthly_notifications(setup, session, payment_method_code): # pyl ]) def test_send_monthly_notifications_failed(setup, session, payment_method_code): # pylint: disable=unused-argument """Test send monthly statement notifications failure.""" - with app.app_context(): - # create statement, invoice, payment data for previous month - last_month, last_year = get_previous_month_and_year() - previous_month_year = datetime(last_year, last_month, 5) - - account, invoice, inv_ref, payment, \ - statement_recipient, statement_settings = create_test_data(payment_method_code, - previous_month_year, - StatementFrequency.MONTHLY.value) - - assert invoice.payment_method_code == payment_method_code - assert account.payment_method == payment_method_code - - # Generate statement for previous month - freeze time to the 1st of the current month - with freeze_time(datetime.now().replace(day=1)): - StatementTask.generate_statements() - - # Assert statements and invoice was created - statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, - limit=100) - assert statements is not None - assert len(statements) == 2 # items results and page total - assert len(statements[0]) == 1 # items - invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) - assert invoices is not None - assert invoices[0].invoice_id == invoice.id - - # Assert notification send_email was invoked - with patch.object(StatementNotificationTask, 'send_email', return_value=False) as mock_mailer: - with patch('tasks.statement_notification_task.get_token') as mock_get_token: - mock_get_token.return_value = 'mock_token' - StatementNotificationTask.send_notifications() - mock_get_token.assert_called_once() - # Assert token and email recipient - mock any for HTML generated - mock_mailer.assert_called_with(mock_get_token.return_value, statement_recipient.email, ANY) - - # Assert statement notification code indicates failed - statement: Statement = Statement.find_by_id(statements[0][0].id) - assert statement is not None - assert statement.notification_status_code == NotificationStatus.FAILED.value + # create statement, invoice, payment data for previous month + last_month, last_year = get_previous_month_and_year() + previous_month_year = datetime(last_year, last_month, 5) + + account, invoice, inv_ref, payment, \ + statement_recipient, statement_settings = create_test_data(payment_method_code, + previous_month_year, + StatementFrequency.MONTHLY.value) + + assert invoice.payment_method_code == payment_method_code + assert account.payment_method == payment_method_code + + # Generate statement for previous month - freeze time to the 1st of the current month + with freeze_time(datetime.now().replace(day=1)): + StatementTask.generate_statements() + + # Assert statements and invoice was created + statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, + limit=100) + assert statements is not None + assert len(statements) == 2 # items results and page total + assert len(statements[0]) == 1 # items + invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) + assert invoices is not None + assert invoices[0].invoice_id == invoice.id + + # Assert notification send_email was invoked + with patch.object(StatementNotificationTask, 'send_email', return_value=False) as mock_mailer: + with patch('tasks.statement_notification_task.get_token') as mock_get_token: + mock_get_token.return_value = 'mock_token' + StatementNotificationTask.send_notifications() + mock_get_token.assert_called_once() + # Assert token and email recipient - mock any for HTML generated + mock_mailer.assert_called_with(mock_get_token.return_value, statement_recipient.email, ANY) + + # Assert statement notification code indicates failed + statement: Statement = Statement.find_by_id(statements[0][0].id) + assert statement is not None + assert statement.notification_status_code == NotificationStatus.FAILED.value def test_send_eft_notifications(setup, session): # pylint: disable=unused-argument """Test send monthly EFT statement notifications.""" - with app.app_context(): - # create statement, invoice, payment data for previous month - last_month, last_year = get_previous_month_and_year() - previous_month_year = datetime(last_year, last_month, 5) - account, invoice, inv_ref, payment, \ - statement_recipient, statement_settings = create_test_data(PaymentMethod.EFT.value, - previous_month_year, - StatementFrequency.MONTHLY.value, - 351.50) - - assert invoice.payment_method_code == PaymentMethod.EFT.value - assert account.payment_method == PaymentMethod.EFT.value - - # Generate statement for previous month - freeze time to the 1st of the current month - with freeze_time(datetime.now().replace(day=1)): - StatementTask.generate_statements() - - # Assert statements and invoice was created - statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, - limit=100) - assert statements is not None - assert len(statements) == 2 # items results and page total - assert len(statements[0]) == 1 # items - invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) - assert invoices is not None - assert invoices[0].invoice_id == invoice.id - - # Assert notification was published to the mailer queue - with patch('tasks.statement_notification_task.publish_statement_notification') as mock_mailer: - with patch('tasks.statement_notification_task.get_token') as mock_get_token: - mock_get_token.return_value = 'mock_token' - StatementNotificationTask.send_notifications() - mock_get_token.assert_called_once() - mock_mailer.assert_called_once_with(account, statements[0][0], 351.5, statement_recipient.email) - - # Assert statement notification code indicates success - statement: Statement = Statement.find_by_id(statements[0][0].id) - assert statement is not None - assert statement.notification_status_code == NotificationStatus.SUCCESS.value + # create statement, invoice, payment data for previous month + last_month, last_year = get_previous_month_and_year() + previous_month_year = datetime(last_year, last_month, 5) + account, invoice, inv_ref, payment, \ + statement_recipient, statement_settings = create_test_data(PaymentMethod.EFT.value, + previous_month_year, + StatementFrequency.MONTHLY.value, + 351.50) + + assert invoice.payment_method_code == PaymentMethod.EFT.value + assert account.payment_method == PaymentMethod.EFT.value + + # Generate statement for previous month - freeze time to the 1st of the current month + with freeze_time(datetime.now().replace(day=1)): + StatementTask.generate_statements() + + # Assert statements and invoice was created + statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, + limit=100) + assert statements is not None + assert len(statements) == 2 # items results and page total + assert len(statements[0]) == 1 # items + invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) + assert invoices is not None + assert invoices[0].invoice_id == invoice.id + + # Assert notification was published to the mailer queue + with patch('tasks.statement_notification_task.publish_statement_notification') as mock_mailer: + with patch('tasks.statement_notification_task.get_token') as mock_get_token: + mock_get_token.return_value = 'mock_token' + StatementNotificationTask.send_notifications() + mock_get_token.assert_called_once() + mock_mailer.assert_called_once_with(account, statements[0][0], 351.5, statement_recipient.email) + + # Assert statement notification code indicates success + statement: Statement = Statement.find_by_id(statements[0][0].id) + assert statement is not None + assert statement.notification_status_code == NotificationStatus.SUCCESS.value def test_send_eft_notifications_failure(setup, session): # pylint: disable=unused-argument """Test send monthly EFT statement notifications failure.""" - with app.app_context(): - # create statement, invoice, payment data for previous month - last_month, last_year = get_previous_month_and_year() - previous_month_year = datetime(last_year, last_month, 5) - account, invoice, inv_ref, payment, \ - statement_recipient, statement_settings = create_test_data(PaymentMethod.EFT.value, - previous_month_year, - StatementFrequency.MONTHLY.value, - 351.50) - - assert invoice.payment_method_code == PaymentMethod.EFT.value - assert account.payment_method == PaymentMethod.EFT.value - - # Generate statement for previous month - freeze time to the 1st of the current month - with freeze_time(datetime.now().replace(day=1)): - StatementTask.generate_statements() - - # Assert statements and invoice was created - statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, - limit=100) - assert statements is not None - assert len(statements) == 2 # items results and page total - assert len(statements[0]) == 1 # items - invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) - assert invoices is not None - assert invoices[0].invoice_id == invoice.id - - # Assert notification was published to the mailer queue - with patch('tasks.statement_notification_task.publish_statement_notification') as mock_mailer: - mock_mailer.side_effect = Exception('Mock Exception') - with patch('tasks.statement_notification_task.get_token') as mock_get_token: - mock_get_token.return_value = 'mock_token' - StatementNotificationTask.send_notifications() - mock_get_token.assert_called_once() - mock_mailer.assert_called_once_with(account, statements[0][0], 351.5, statement_recipient.email) - - # Assert statement notification code indicates failed - statement: Statement = Statement.find_by_id(statements[0][0].id) - assert statement is not None - assert statement.notification_status_code == NotificationStatus.FAILED.value + # create statement, invoice, payment data for previous month + last_month, last_year = get_previous_month_and_year() + previous_month_year = datetime(last_year, last_month, 5) + account, invoice, inv_ref, payment, \ + statement_recipient, statement_settings = create_test_data(PaymentMethod.EFT.value, + previous_month_year, + StatementFrequency.MONTHLY.value, + 351.50) + + assert invoice.payment_method_code == PaymentMethod.EFT.value + assert account.payment_method == PaymentMethod.EFT.value + + # Generate statement for previous month - freeze time to the 1st of the current month + with freeze_time(datetime.now().replace(day=1)): + StatementTask.generate_statements() + + # Assert statements and invoice was created + statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, + limit=100) + assert statements is not None + assert len(statements) == 2 # items results and page total + assert len(statements[0]) == 1 # items + invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) + assert invoices is not None + assert invoices[0].invoice_id == invoice.id + + # Assert notification was published to the mailer queue + with patch('tasks.statement_notification_task.publish_statement_notification') as mock_mailer: + mock_mailer.side_effect = Exception('Mock Exception') + with patch('tasks.statement_notification_task.get_token') as mock_get_token: + mock_get_token.return_value = 'mock_token' + StatementNotificationTask.send_notifications() + mock_get_token.assert_called_once() + mock_mailer.assert_called_once_with(account, statements[0][0], 351.5, statement_recipient.email) + + # Assert statement notification code indicates failed + statement: Statement = Statement.find_by_id(statements[0][0].id) + assert statement is not None + assert statement.notification_status_code == NotificationStatus.FAILED.value def test_send_eft_notifications_ff_disabled(setup, session): # pylint: disable=unused-argument """Test send monthly EFT statement notifications failure.""" - with app.app_context(): - # create statement, invoice, payment data for previous month - last_month, last_year = get_previous_month_and_year() - previous_month_year = datetime(last_year, last_month, 5) - account, invoice, inv_ref, payment, \ - statement_recipient, statement_settings = create_test_data(PaymentMethod.EFT.value, - previous_month_year, - StatementFrequency.MONTHLY.value, - 351.50) - - assert invoice.payment_method_code == PaymentMethod.EFT.value - assert account.payment_method == PaymentMethod.EFT.value - - # Generate statement for previous month - freeze time to the 1st of the current month - with freeze_time(datetime.now().replace(day=1)): - StatementTask.generate_statements() - - # Assert statements and invoice was created - statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, - limit=100) - assert statements is not None - assert len(statements) == 2 # items results and page total - assert len(statements[0]) == 1 # items - invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) - assert invoices is not None - assert invoices[0].invoice_id == invoice.id - - # Assert notification was published to the mailer queue - with patch('tasks.statement_notification_task.publish_statement_notification') as mock_mailer: - with patch('tasks.statement_notification_task.get_token') as mock_get_token: - with patch('tasks.statement_notification_task.flags.is_on', return_value=False): - mock_get_token.return_value = 'mock_token' - StatementNotificationTask.send_notifications() - mock_get_token.assert_called_once() - mock_mailer.assert_not_called() - - # Assert statement notification code indicates skipped - statement: Statement = Statement.find_by_id(statements[0][0].id) - assert statement is not None - assert statement.notification_status_code == NotificationStatus.SKIP.value + # create statement, invoice, payment data for previous month + last_month, last_year = get_previous_month_and_year() + previous_month_year = datetime(last_year, last_month, 5) + account, invoice, inv_ref, payment, \ + statement_recipient, statement_settings = create_test_data(PaymentMethod.EFT.value, + previous_month_year, + StatementFrequency.MONTHLY.value, + 351.50) + + assert invoice.payment_method_code == PaymentMethod.EFT.value + assert account.payment_method == PaymentMethod.EFT.value + + # Generate statement for previous month - freeze time to the 1st of the current month + with freeze_time(datetime.now().replace(day=1)): + StatementTask.generate_statements() + + # Assert statements and invoice was created + statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, + limit=100) + assert statements is not None + assert len(statements) == 2 # items results and page total + assert len(statements[0]) == 1 # items + invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) + assert invoices is not None + assert invoices[0].invoice_id == invoice.id + + # Assert notification was published to the mailer queue + with patch('tasks.statement_notification_task.publish_statement_notification') as mock_mailer: + with patch('tasks.statement_notification_task.get_token') as mock_get_token: + with patch('tasks.statement_notification_task.flags.is_on', return_value=False): + mock_get_token.return_value = 'mock_token' + StatementNotificationTask.send_notifications() + mock_get_token.assert_called_once() + mock_mailer.assert_not_called() + + # Assert statement notification code indicates skipped + statement: Statement = Statement.find_by_id(statements[0][0].id) + assert statement is not None + assert statement.notification_status_code == NotificationStatus.SKIP.value diff --git a/jobs/payment-jobs/utils/logger.py b/jobs/payment-jobs/utils/logger.py index 8b88ddcf2..8568f87dd 100755 --- a/jobs/payment-jobs/utils/logger.py +++ b/jobs/payment-jobs/utils/logger.py @@ -18,12 +18,7 @@ def setup_logging(conf): - """Create the services logger. - - TODO should be reworked to load in the proper loggers and remove others - """ - # log_file_path = path.join(path.abspath(path.dirname(__file__)), conf) - + """Create the services logger.""" if conf and path.isfile(conf): logging.config.fileConfig(conf) print(f'Configure logging, from conf:{conf}', file=sys.stdout) diff --git a/jobs/payment-jobs/utils/mailer.py b/jobs/payment-jobs/utils/mailer.py index 1420b6c35..69cf1a6b9 100644 --- a/jobs/payment-jobs/utils/mailer.py +++ b/jobs/payment-jobs/utils/mailer.py @@ -14,7 +14,6 @@ """Task to activate accounts with pending activation.Mostly for PAD with 3 day activation period.""" from dataclasses import dataclass from datetime import datetime -from typing import Dict from flask import current_app from pay_api.models import FeeSchedule as FeeScheduleModel @@ -133,4 +132,3 @@ def publish_payment_notification(info: StatementNotificationInfo) -> bool: return False return True - diff --git a/pay-admin/Dockerfile b/pay-admin/Dockerfile index 7ec36797c..ad6a87170 100644 --- a/pay-admin/Dockerfile +++ b/pay-admin/Dockerfile @@ -1,33 +1,88 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye as development_build ARG VCS_REF="missing" ARG BUILD_DATE="missing" ENV VCS_REF=${VCS_REF} ENV BUILD_DATE=${BUILD_DATE} +ENV PORT=8080 LABEL org.label-schema.vcs-ref=${VCS_REF} \ org.label-schema.build-date=${BUILD_DATE} USER root -# Create working directory -RUN mkdir /opt/app-root && chmod 755 /opt/app-root -WORKDIR /opt/app-root +LABEL maintainer="travissemple" +LABEL vendor="BCROS" -# Install the requirements -COPY ./requirements.txt . +ARG APP_ENV \ + # Needed for fixing permissions of files created by Docker: + UID=1000 \ + GID=1000 -RUN pip install --upgrade pip -RUN pip install --no-cache-dir -r requirements.txt +ENV APP_ENV=${APP_ENV} \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_DEFAULT_TIMEOUT=100 \ + PIP_ROOT_USER_ACTION=ignore \ + # poetry: + POETRY_VERSION=1.8.3 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + POETRY_HOME='/usr/local' -COPY . . +SHELL ["/bin/bash", "-eo", "pipefail", "-c"] -RUN pip install . +RUN apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y \ + bash \ + brotli \ + build-essential \ + curl \ + gettext \ + git \ + libpq-dev \ + wait-for-it \ + && curl -sSL 'https://install.python-poetry.org' | python - \ + && poetry --version \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* -USER 1001 +WORKDIR /code -# Set Python path -ENV PYTHONPATH=/opt/app-root/src +RUN groupadd -g "${GID}" -r web \ + && useradd -d '/code' -g web -l -r -u "${UID}" web \ + && chown web:web -R '/code' -ENTRYPOINT ["bash", "docker-entrypoint.sh"] +# Copy only requirements, to cache them in docker layer +COPY --chown=web:web ./poetry.lock ./pyproject.toml /code/ + +COPY --chown=web:web ./admin /code/admin +COPY --chown=web:web ./README.md /code + +# Project initialization: +RUN --mount=type=cache,target="$POETRY_CACHE_DIR" \ + echo "$APP_ENV" \ + && poetry version \ + # Install deps: + && poetry run pip install -U pip \ + && poetry install \ + $(if [ -z ${APP_ENV+x} ] | [ "$APP_ENV" = 'production' ]; then echo '--only main'; fi) \ + --no-interaction --no-ansi + +# Running as non-root user: +USER web + +# The following stage is only for production: +FROM development_build AS production_build +COPY --chown=web:web . /code + +CMD gunicorn --bind 0.0.0.0:${PORT} --config /code/gunicorn_config.py wsgi:app diff --git a/pay-admin/Makefile b/pay-admin/Makefile index b6ed852ea..c8bffa188 100755 --- a/pay-admin/Makefile +++ b/pay-admin/Makefile @@ -12,7 +12,7 @@ DOCKER_NAME:=pay-admin ################################################################################# # COMMANDS -- Setup # ################################################################################# -setup: install install-dev ## Setup the project +setup: install ## Setup the project clean: clean-build clean-pyc clean-test ## Clean the project rm -rf venv/ @@ -36,19 +36,9 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements/prod.txt ;\ - pip freeze | sort > requirements.txt ;\ - cat requirements/repo-libraries.txt >> requirements.txt ;\ - pip install -Ur requirements/repo-libraries.txt ;\ - pip install -Ur requirements/dev.txt ;\ - touch venv/bin/activate # update so it's as new as requirements/prod.txt - -install-dev: ## Install local application - . venv/bin/activate && pip install -e . +install: clean + pip install poetry ;\ + poetry install ################################################################################# # COMMANDS - CI # @@ -56,15 +46,15 @@ install-dev: ## Install local application ci: lint flake8 test ## CI flow pylint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg $(PROJECT_NAME) + poetry run pylint --rcfile=setup.cfg $(PROJECT_NAME) flake8: ## Linting with flake8 - . venv/bin/activate && flake8 $(PROJECT_NAME) tests + poetry run flake8 $(PROJECT_NAME) tests lint: pylint flake8 ## run all lint type scripts test: ## Unit testing - . venv/bin/activate && pytest + poetry run pytest mac-cov: test ## Run the coverage report and display in a browser window (mac) @open -a "Google Chrome" htmlcov/index.html @@ -125,7 +115,7 @@ tag: push ## tag image # COMMANDS - Local # ################################################################################# run: ## Run the project in local - . venv/bin/activate && python -m flask run -p 5000 + poetry run flask run -p 5000 ################################################################################# # Self Documenting Commands # diff --git a/pay-admin/admin/__init__.py b/pay-admin/admin/__init__.py index 7dd1f3e92..411a5a317 100755 --- a/pay-admin/admin/__init__.py +++ b/pay-admin/admin/__init__.py @@ -33,7 +33,7 @@ setup_logging(os.path.join(_Config.PROJECT_ROOT, 'logging.conf')) -def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): +def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')): """Return a configured Flask App using the Factory method.""" app = Flask(__name__) app.config.from_object(config.CONFIGURATION[run_mode]) diff --git a/pay-admin/admin/config.py b/pay-admin/admin/config.py index 5caa3687a..ea9fabc0f 100755 --- a/pay-admin/admin/config.py +++ b/pay-admin/admin/config.py @@ -58,7 +58,6 @@ def _get_config(config_key: str, **kwargs): value = os.getenv(config_key, kwargs.get('default')) else: value = os.getenv(config_key) - # assert value TODO Un-comment once we find a solution to run pre-hook without initializing app return value @@ -77,13 +76,17 @@ class _Config(): # pylint: disable=too-few-public-methods DB_NAME = _get_config('DATABASE_NAME') DB_HOST = _get_config('DATABASE_HOST') DB_PORT = _get_config('DATABASE_PORT', default='5432') - SQLALCHEMY_DATABASE_URI = f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' + if DB_UNIX_SOCKET := os.getenv('DATABASE_UNIX_SOCKET', None): + SQLALCHEMY_DATABASE_URI = ( + f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432' + ) + else: + SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' SQLALCHEMY_ECHO = _get_config('SQLALCHEMY_ECHO', default='False').lower() == 'true' # Normal Keycloak parameters. OIDC_CLIENT_SECRETS = os.getenv('PAY_OIDC_CLIENT_SECRETS', 'secrets/keycloak.json') OIDC_SCOPES = ['openid', 'email', 'profile'] - OIDC_VALID_ISSUERS = [os.getenv('PAY_OIDC_VALID_ISSUERS')] # Undocumented Keycloak parameter: allows sending cookies without the secure flag, which we need for the local # non-TLS HTTP server. Set this to non-"True" for local development, and use the default everywhere else. OIDC_ID_TOKEN_COOKIE_SECURE = os.getenv('PAY_OIDC_ID_TOKEN_COOKIE_SECURE', 'True').lower() == 'true' @@ -113,10 +116,7 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods DB_NAME = _get_config('DATABASE_TEST_NAME', default='paytestdb') DB_HOST = _get_config('DATABASE_TEST_HOST', default='localhost') DB_PORT = _get_config('DATABASE_TEST_PORT', default='5432') - SQLALCHEMY_DATABASE_URI = _get_config( - 'DATABASE_TEST_URL', - default=f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' - ) + SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' class ProdConfig(_Config): # pylint: disable=too-few-public-methods diff --git a/pay-admin/devops/gcp/clouddeploy-targets.yaml b/pay-admin/devops/gcp/clouddeploy-targets.yaml new file mode 100644 index 000000000..2dca1c847 --- /dev/null +++ b/pay-admin/devops/gcp/clouddeploy-targets.yaml @@ -0,0 +1,104 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: dev +description: Dev Environment +deployParameters: + deploy-env: "development" + deploy-project-id: "gtksf3-dev" + service-name: "pay-admin-dev" + container-name: "pay-admin-dev" + app-env: "dev" + cloudsql-instances: "gtksf3-dev:northamerica-northeast1:pay-db-dev" + service-account: "sa-api@gtksf3-dev.iam.gserviceaccount.com" +run: + location: projects/gtksf3-dev/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: test +description: Test Environment +deployParameters: + deploy-env: "development" + deploy-project-id: "gtksf3-test" + service-name: "pay-admin-test" + container-name: "pay-admin-test" + app-env: "test" + cloudsql-instances: "gtksf3-test:northamerica-northeast1:pay-db-test" + service-account: "sa-api@gtksf3-test.iam.gserviceaccount.com" +run: + location: projects/gtksf3-test/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: sandbox +description: Sandbox Environment +requireApproval: true +deployParameters: + deploy-env: "production" + deploy-project-id: "gtksf3-tools" + service-name: "pay-admin-sandbox" + container-name: "pay-admin-sandbox" + app-env: "sandbox" + cloudsql-instances: "gtksf3-tools:northamerica-northeast1:pay-db-sandbox" + service-account: "sa-api@gtksf3-tools.iam.gserviceaccount.com" + max-scale: "50" + container-concurrency: "20" + container-port: "8080" + resources-cpu: 4000m + resources-memory: 8Gi +run: + location: projects/gtksf3-tools/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: prod +description: Production Environment +requireApproval: true +deployParameters: + deploy-env: "production" + deploy-project-id: "gtksf3-prod" + service-name: "pay-admin-prod" + container-name: "pay-admin-prod" + app-env: "production" + cloudsql-instances: "gtksf3-prod:northamerica-northeast1:pay-db-prod" + service-account: "sa-api@gtksf3-prod.iam.gserviceaccount.com" + max-scale: "50" + container-concurrency: "20" + container-port: "8080" + resources-cpu: 4000m + resources-memory: 8Gi +run: + location: projects/gtksf3-prod/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' \ No newline at end of file diff --git a/pay-admin/devops/vaults.gcp.env b/pay-admin/devops/vaults.gcp.env new file mode 100644 index 000000000..5cc42836e --- /dev/null +++ b/pay-admin/devops/vaults.gcp.env @@ -0,0 +1,7 @@ +DATABASE_NAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_NAME" +DATABASE_PASSWORD="op://database/$APP_ENV/pay-db-gcp/DATABASE_PASSWORD" +DATABASE_PORT="op://database/$APP_ENV/pay-db-gcp/DATABASE_PORT" +DATABASE_UNIX_SOCKET="op://database/$APP_ENV/pay-db-gcp/DATABASE_UNIX_SOCKET" +DATABASE_USERNAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_USERNAME" +PAY_OIDC_CLIENT_SECRETS="op://relationship/$APP_ENV/pay-admin/PAY_OIDC_CLIENT_SECRETS" +PAY_OIDC_ID_TOKEN_COOKIE_SECURE="op://relationship/$APP_ENV/pay-admin/PAY_OIDC_ID_TOKEN_COOKIE_SECURE" \ No newline at end of file diff --git a/pay-admin/devops/vaults.json b/pay-admin/devops/vaults.json deleted file mode 100644 index 11934b144..000000000 --- a/pay-admin/devops/vaults.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - "vault": "relationship", - "application": [ - "postgres-pay", - "pay-admin" - ] - }, - { - "vault": "sentry", - "application": [ - "relationship-api" - ] - } -] diff --git a/pay-admin/openshift/templates/pay-admin-build.json b/pay-admin/openshift/templates/pay-admin-build.json deleted file mode 100644 index 51676aab7..000000000 --- a/pay-admin/openshift/templates/pay-admin-build.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Build template for a pay-admin service.", - "tags": "flask", - "iconClass": "icon-python" - }, - "name": "${NAME}-build" - }, - "objects": [ - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}" - } - }, - { - "kind": "BuildConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}", - "labels": { - "app": "${NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-build" - } - }, - "spec": { - "source": { - "type": "Git", - "git": { - "uri": "${GIT_REPO_URL}", - "ref": "${GIT_REF}" - }, - "contextDir": "${SOURCE_CONTEXT_DIR}" - }, - "strategy": { - "type": "Docker", - "dockerStrategy": { - "dockerfilePath": "${DOCKER_FILE_PATH}" - } - }, - "output": { - "to": { - "kind": "ImageStreamTag", - "name": "${NAME}:${OUTPUT_IMAGE_TAG}" - } - }, - "triggers": [ - { - "type": "ConfigChange" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the objects defined in this template. You should keep this as default unless your know what your doing.", - "required": true, - "value": "pay-admin" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "GIT_REPO_URL", - "displayName": "Git Repo URL", - "description": "The URL to your GIT repo, don't use the this default unless your just experimenting.", - "required": true, - "value": "https://github.com/bcgov/sbc-pay.git" - }, - { - "name": "GIT_REF", - "displayName": "Git Reference", - "description": "The git reference or branch.", - "required": true, - "value": "main" - }, - { - "name": "SOURCE_CONTEXT_DIR", - "displayName": "Source Context Directory", - "description": "The source context directory.", - "required": true, - "value": "pay-admin" - }, - { - "name": "OUTPUT_IMAGE_TAG", - "displayName": "Output Image Tag", - "description": "The tag given to the built image.", - "required": true, - "value": "latest" - }, - { - "name": "DOCKER_FILE_PATH", - "displayName": "Docker File Path", - "description": "The path to the docker file defining the build.", - "required": false, - "value": "Dockerfile" - } - ] -} diff --git a/pay-admin/openshift/templates/pay-admin-deploy.json b/pay-admin/openshift/templates/pay-admin-deploy.json deleted file mode 100644 index 76948762d..000000000 --- a/pay-admin/openshift/templates/pay-admin-deploy.json +++ /dev/null @@ -1,316 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for a pay admin service.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "apps.openshift.io/v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "environment": "${TAG_NAME}", - "name": "${NAME}", - "role": "api" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%" - }, - "resources": { - }, - "activeDeadlineSeconds": 21600 - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "78c88a-tools", - "name": "${NAME}:${TAG_NAME}" - }, - "lastTriggeredImage": "image-registry.openshift-image-registry.svc:5000/78c88a-tools/${NAME}@sha256:66652d7231c3257c3b6752c58775ece775c9ddea3ef1e3423476c1c5fc4af99e" - } - } - ], - "replicas": 1, - "revisionHistoryLimit": 10, - "test": false, - "selector": { - "environment": "dev", - "name": "${NAME}" - }, - "template": { - "metadata": { - "creationTimestamp": null, - "labels": { - "environment": "dev", - "name": "${NAME}", - "role": "api" - } - }, - "spec": { - "volumes": [ - { - "configMap": { - "defaultMode": 420, - "name": "${NAME}-keycloak-configuration" - }, - "name": "${NAME}-keycloak-configuration" - } - ], - "containers": [ - { - "resources": { - "limits": { - "cpu": "750m", - "memory": "2Gi" - }, - "requests": { - "cpu": "100m", - "memory": "100Mi" - } - }, - "readinessProbe": { - "httpGet": { - "path": "/admin", - "port": 8080, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "terminationMessagePath": "/dev/termination-log", - "name": "${NAME}-${TAG_NAME}", - "livenessProbe": { - "httpGet": { - "path": "/admin", - "port": 8080, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "env": null, - "ports": [ - { - "containerPort": 8080, - "protocol": "TCP" - } - ], - "imagePullPolicy": "Always", - "volumeMounts": [ - { - "name": "${NAME}-keycloak-configuration", - "readOnly": true, - "mountPath": "/app/config" - } - ], - "terminationMessagePolicy": "File", - "image": "image-registry.openshift-image-registry.svc:5000/78c88a-tools/${NAME}@sha256:66652d7231c3257c3b6752c58775ece775c9ddea3ef1e3423476c1c5fc4af99e" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": { - }, - "schedulerName": "default-scheduler" - } - } - }, - "status": { - "observedGeneration": 5940, - "details": { - "message": "image change", - "causes": [ - { - "type": "ImageChange", - "imageTrigger": { - "from": { - "kind": "DockerImage", - "name": "image-registry.openshift-image-registry.svc:5000/78c88a-tools/${NAME}@sha256:66652d7231c3257c3b6752c58775ece775c9ddea3ef1e3423476c1c5fc4af99e" - } - } - } - ] - }, - "availableReplicas": 1, - "unavailableReplicas": 0, - "latestVersion": 271, - "updatedReplicas": 1, - "replicas": 1, - "readyReplicas": 1 - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app-group": "${APP_GROUP}", - "environment": "${TAG_NAME}", - "name": "${NAME}", - "role": "api" - } - }, - "spec": { - "ports": [ - { - "name": "8080-tcp", - "protocol": "TCP", - "port": 8080, - "targetPort": 8080 - } - ], - "selector": { - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - }, - { - "kind": "Route", - "apiVersion": "route.openshift.io/v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "environment": "${TAG_NAME}", - "name": "${NAME}", - "role": "api" - } - }, - "spec": { - "host": "${NAME}-${TAG_NAME}.apps.silver.devops.gov.bc.ca", - "to": { - "kind": "Service", - "name": "${NAME}-${TAG_NAME}", - "weight": 100 - }, - "port": { - "targetPort": "8080-tcp" - }, - "tls": { - "termination": "edge", - "insecureEdgeTerminationPolicy": "Redirect" - }, - "wildcardPolicy": "None" - }, - "status": { - "ingress": [ - { - "host": "${NAME}-${TAG_NAME}.apps.silver.devops.gov.bc.ca", - "routerName": "default", - "conditions": [ - { - "type": "Admitted", - "status": "True" - } - ], - "wildcardPolicy": "None", - "routerCanonicalHostname": "apps.silver.devops.gov.bc.ca" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "pay-admin" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - } - ] -} \ No newline at end of file diff --git a/pay-admin/poetry.lock b/pay-admin/poetry.lock new file mode 100644 index 000000000..67fd517ef --- /dev/null +++ b/pay-admin/poetry.lock @@ -0,0 +1,2473 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "authlib" +version = "1.3.0" +description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Authlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:9637e4de1fb498310a56900b3e2043a206b03cb11c05422014b0302cbc814be3"}, + {file = "Authlib-1.3.0.tar.gz", hash = "sha256:959ea62a5b7b5123c5059758296122b57cd2585ae2ed1c0622c21b371ffdae06"}, +] + +[package.dependencies] +cryptography = "*" + +[[package]] +name = "autopep8" +version = "2.0.4" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.6" +files = [ + {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, + {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, +] + +[package.dependencies] +pycodestyle = ">=2.10.0" + +[[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 = "cachelib" +version = "0.9.0" +description = "A collection of cache libraries in the same API interface." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachelib-0.9.0-py3-none-any.whl", hash = "sha256:811ceeb1209d2fe51cd2b62810bd1eccf70feba5c52641532498be5c675493b3"}, + {file = "cachelib-0.9.0.tar.gz", hash = "sha256:38222cc7c1b79a23606de5c2607f4925779e37cdcea1c2ad21b8bae94b5425a5"}, +] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "cattrs" +version = "23.2.3" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"}, + {file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"}, +] + +[package.dependencies] +attrs = ">=23.1.0" + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +orjson = ["orjson (>=3.9.2)"] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.4.3" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "croniter" +version = "2.0.2" +description = "croniter provides iteration for datetime object with cron like format" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "croniter-2.0.2-py2.py3-none-any.whl", hash = "sha256:78bf110a2c7dbbfdd98b926318ae6c64a731a4c637c7befe3685755110834746"}, + {file = "croniter-2.0.2.tar.gz", hash = "sha256:8bff16c9af4ef1fb6f05416973b8f7cb54997c02f2f8365251f9bf1dded91866"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = ">2021.1" + +[[package]] +name = "cryptography" +version = "42.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "dpath" +version = "2.1.6" +description = "Filesystem-like pathing and searching for dictionaries" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dpath-2.1.6-py3-none-any.whl", hash = "sha256:31407395b177ab63ef72e2f6ae268c15e938f2990a8ecf6510f5686c02b6db73"}, + {file = "dpath-2.1.6.tar.gz", hash = "sha256:f1e07c72e8605c6a9e80b64bc8f42714de08a789c7de417e49c3f87a19692e47"}, +] + +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "expiringdict" +version = "1.2.2" +description = "Dictionary with auto-expiring values for caching purposes" +optional = false +python-versions = "*" +files = [ + {file = "expiringdict-1.2.2-py3-none-any.whl", hash = "sha256:09a5d20bc361163e6432a874edd3179676e935eb81b925eccef48d409a8a45e8"}, + {file = "expiringdict-1.2.2.tar.gz", hash = "sha256:300fb92a7e98f15b05cf9a856c1415b3bc4f2e132be07daa326da6414c23ee09"}, +] + +[package.extras] +tests = ["coverage", "coveralls", "dill", "mock", "nose"] + +[[package]] +name = "flake8" +version = "7.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "flake8-blind-except" +version = "0.2.1" +description = "A flake8 extension that checks for blind except: statements" +optional = false +python-versions = "*" +files = [ + {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, +] + +[[package]] +name = "flake8-debugger" +version = "4.1.2" +description = "ipdb/pdb statement checker plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-isort" +version = "6.1.1" +description = "flake8 plugin that integrates isort" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_isort-6.1.1-py3-none-any.whl", hash = "sha256:0fec4dc3a15aefbdbe4012e51d5531a2eb5fa8b981cdfbc882296a59b54ede12"}, + {file = "flake8_isort-6.1.1.tar.gz", hash = "sha256:c1f82f3cf06a80c13e1d09bfae460e9666255d5c780b859f19f8318d420370b3"}, +] + +[package.dependencies] +flake8 = "*" +isort = ">=5.0.0,<6" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "flake8-quotes" +version = "3.4.0" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, +] + +[package.dependencies] +flake8 = "*" +setuptools = "*" + +[[package]] +name = "flask" +version = "3.0.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, +] + +[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 = "flask-admin" +version = "1.6.1" +description = "Simple and extensible admin interface framework for Flask" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Admin-1.6.1.tar.gz", hash = "sha256:24cae2af832b6a611a01d7dc35f42d266c1d6c75a426b869d8cb241b78233369"}, + {file = "Flask_Admin-1.6.1-py3-none-any.whl", hash = "sha256:fd8190f1ec3355913a22739c46ed3623f1d82b8112cde324c60a6fc9b21c9406"}, +] + +[package.dependencies] +Flask = ">=0.7" +wtforms = "*" + +[package.extras] +aws = ["boto"] +azure = ["azure-storage-blob"] + +[[package]] +name = "flask-caching" +version = "2.1.0" +description = "Adds caching support to Flask applications." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, + {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, +] + +[package.dependencies] +cachelib = ">=0.9.0,<0.10.0" +Flask = "*" + +[[package]] +name = "flask-cors" +version = "4.0.0" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Cors-4.0.0.tar.gz", hash = "sha256:f268522fcb2f73e2ecdde1ef45e2fd5c71cc48fe03cffb4b441c6d1b40684eb0"}, + {file = "Flask_Cors-4.0.0-py2.py3-none-any.whl", hash = "sha256:bc3492bfd6368d27cfe79c7821df5a8a319e1a6d5eab277a3794be19bdc51783"}, +] + +[package.dependencies] +Flask = ">=0.9" + +[[package]] +name = "flask_jwt_oidc" +version = "0.3.0" +description = "Flask JWT OIDC" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +cachelib = "*" +flask = "*" +python-jose = "*" +six = "*" + +[package.source] +type = "git" +url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +reference = "HEAD" +resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" + +[[package]] +name = "flask-marshmallow" +version = "1.2.0" +description = "Flask + marshmallow for beautiful APIs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_marshmallow-1.2.0-py3-none-any.whl", hash = "sha256:ddd2a7c8db5e00a8d56c8ca5f651efae1de7d76b7d821b56ccc2caf09135ad12"}, + {file = "flask_marshmallow-1.2.0.tar.gz", hash = "sha256:d0f79eb9743f0c530a3d9e848503e1f2228e6b35a819c91e913af02e68421805"}, +] + +[package.dependencies] +Flask = ">=2.2" +marshmallow = ">=3.0.0" + +[package.extras] +dev = ["flask-marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["Sphinx (==7.2.6)", "marshmallow-sqlalchemy (>=0.19.0)", "sphinx-issues (==4.0.0)"] +sqlalchemy = ["flask-sqlalchemy (>=3.0.0)", "marshmallow-sqlalchemy (>=0.29.0)"] +tests = ["flask-marshmallow[sqlalchemy]", "pytest"] + +[[package]] +name = "flask-migrate" +version = "4.0.7" +description = "SQLAlchemy database migrations for Flask applications using Alembic." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Migrate-4.0.7.tar.gz", hash = "sha256:dff7dd25113c210b069af280ea713b883f3840c1e3455274745d7355778c8622"}, + {file = "Flask_Migrate-4.0.7-py3-none-any.whl", hash = "sha256:5c532be17e7b43a223b7500d620edae33795df27c75811ddf32560f7d48ec617"}, +] + +[package.dependencies] +alembic = ">=1.9.0" +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" + +[[package]] +name = "flask-moment" +version = "1.0.5" +description = "Formatting of dates and times in Flask templates using moment.js." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Moment-1.0.5.tar.gz", hash = "sha256:33307ecd4af8290b6df6a9828ff55ac0977d0567817f9bc0f69803d22ed2b55c"}, + {file = "Flask_Moment-1.0.5-py3-none-any.whl", hash = "sha256:6e7b3eef89e2137bbbee975405f241a68a44edfa34bf052c92d84364992adca6"}, +] + +[package.dependencies] +Flask = "*" +packaging = ">=14.1" + +[[package]] +name = "flask-oidc" +version = "2.1.1" +description = "OpenID Connect extension for Flask" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "flask_oidc-2.1.1-py3-none-any.whl", hash = "sha256:379be8fa15c275a2cbbb0e179eb1e937d3c9cf0788f39bad047cdd99f40613b1"}, + {file = "flask_oidc-2.1.1.tar.gz", hash = "sha256:9a283cf760f9d3053ef71280705fbefeb62d1baa0fdceb4a99784ab01ba8e307"}, +] + +[package.dependencies] +authlib = ">=1.2.0,<2.0.0" +flask = ">=2.0.0,<4.0.0" +requests = ">=2.24.0,<3.0.0" + +[[package]] +name = "flask-opentracing" +version = "1.1.0" +description = "OpenTracing support for Flask applications" +optional = false +python-versions = "*" +files = [ + {file = "Flask-OpenTracing-1.1.0.tar.gz", hash = "sha256:a9a39d367fbe7e9ed9c77b90ac48159c1a3e82982a5abf84d3f4d710d24580ac"}, +] + +[package.dependencies] +Flask = "*" +opentracing = ">=2.0,<3" + +[package.extras] +tests = ["flake8", "flake8-quotes", "mock", "pytest", "pytest-cov"] + +[[package]] +name = "flask-script" +version = "2.0.6" +description = "Scripting support for Flask" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Script-2.0.6.tar.gz", hash = "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65"}, +] + +[package.dependencies] +Flask = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +description = "Add SQLAlchemy support to your Flask application." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[package.dependencies] +flask = ">=2.2.5" +sqlalchemy = ">=2.0.16" + +[[package]] +name = "freezegun" +version = "1.4.0" +description = "Let your Python tests travel through time" +optional = false +python-versions = ">=3.7" +files = [ + {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, + {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + +[[package]] +name = "google-api-core" +version = "2.17.1" +description = "Google API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, + {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-auth" +version = "2.28.1" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, + {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-cloud-pubsub" +version = "2.20.0" +description = "Google Cloud Pub/Sub API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, + {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<3.0.0dev" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +grpcio = ">=1.51.3,<2.0dev" +grpcio-status = ">=1.33.2" +proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +libcst = ["libcst (>=0.3.10)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.63.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, + {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.13.0" +description = "IAM API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.13.0.tar.gz", hash = "sha256:fad318608b9e093258fbf12529180f400d1c44453698a33509cc6ecf005b294e"}, + {file = "grpc_google_iam_v1-0.13.0-py2.py3-none-any.whl", hash = "sha256:53902e2af7de8df8c1bd91373d9be55b0743ec267a7428ea638db3775becae89"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.62.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, + {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, + {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, + {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, + {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, + {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, + {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, + {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, + {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, + {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, + {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, + {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, + {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, + {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, + {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, + {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, + {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, + {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, + {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, + {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, + {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, + {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, + {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, + {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.62.1)"] + +[[package]] +name = "grpcio-status" +version = "1.62.1" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, + {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.62.1" +protobuf = ">=4.21.6" + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "holidays" +version = "0.37" +description = "Generate and work with holidays in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "holidays-0.37-py3-none-any.whl", hash = "sha256:5b8ff8c94c06e3b225762d495e51b8e51205d332f8ad092aab809c4bffa8d123"}, + {file = "holidays-0.37.tar.gz", hash = "sha256:712df71a8d97b04554fa1c9208d219fbf174bad2864263bef24c6dcfa1ded6ff"}, +] + +[package.dependencies] +python-dateutil = "*" + +[[package]] +name = "httplib2" +version = "0.22.0" +description = "A comprehensive HTTP client library." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, + {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, +] + +[package.dependencies] +pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[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 = "jaeger-client" +version = "4.8.0" +description = "Jaeger Python OpenTracing Tracer implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaeger-client-4.8.0.tar.gz", hash = "sha256:3157836edab8e2c209bd2d6ae61113db36f7ee399e66b1dcbb715d87ab49bfe0"}, +] + +[package.dependencies] +opentracing = ">=2.1,<3.0" +threadloop = ">=1,<2" +thrift = "*" +tornado = ">=4.3" + +[package.extras] +tests = ["codecov", "coverage", "flake8", "flake8-quotes", "flake8-typing-imports", "mock", "mypy", "opentracing_instrumentation (>=3,<4)", "prometheus_client (==0.11.0)", "pycurl", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "pytest-localserver", "pytest-timeout", "pytest-tornado", "tchannel (==2.1.0)"] + +[[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 = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "launchdarkly-eventsource" +version = "1.1.1" +description = "LaunchDarkly SSE Client" +optional = false +python-versions = ">=3.7" +files = [ + {file = "launchdarkly_eventsource-1.1.1-py3-none-any.whl", hash = "sha256:3d7e5301bc4b4a744ecdaa10de8bce52c2f3c66a97e9aa10ab11ca81b67fb31b"}, + {file = "launchdarkly_eventsource-1.1.1.tar.gz", hash = "sha256:211791f1267f9b7b0a62a0bb5fc9c5ed1fb4a834440f16be551968dbe772557a"}, +] + +[package.dependencies] +urllib3 = ">=1.26.0,<3" + +[[package]] +name = "launchdarkly-server-sdk" +version = "9.2.2" +description = "LaunchDarkly SDK for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, + {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, +] + +[package.dependencies] +certifi = ">=2018.4.16" +expiringdict = ">=1.1.4" +launchdarkly-eventsource = ">=1.1.0,<2.0.0" +pyRFC3339 = ">=1.0" +semver = ">=2.10.2" +urllib3 = ">=1.26.0,<3" + +[package.extras] +consul = ["python-consul (>=1.0.1)"] +dynamodb = ["boto3 (>=1.9.71)"] +redis = ["redis (>=2.10.5)"] +test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] + +[[package]] +name = "mako" +version = "1.3.2" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, + {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "marshmallow" +version = "3.21.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-sqlalchemy" +version = "1.0.0" +description = "SQLAlchemy integration with the marshmallow (de)serialization library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow_sqlalchemy-1.0.0-py3-none-any.whl", hash = "sha256:f415d57809e3555b6323356589aba91e36e4470f35953d3a10c755ac5c3307df"}, + {file = "marshmallow_sqlalchemy-1.0.0.tar.gz", hash = "sha256:20a0f2fcdd5bddc86444fa01461f17f9b6a12a8ddd4ca8c9b34fe2f2e35d00a2"}, +] + +[package.dependencies] +marshmallow = ">=3.10.0" +SQLAlchemy = ">=1.4.40,<3.0" + +[package.extras] +dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)"] +tests = ["pytest (<8)", "pytest-lazy-fixture (>=0.6.2)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "opentracing" +version = "2.4.0" +description = "OpenTracing API for Python. See documentation at http://opentracing.io" +optional = false +python-versions = "*" +files = [ + {file = "opentracing-2.4.0.tar.gz", hash = "sha256:a173117e6ef580d55874734d1fa7ecb6f3655160b8b8974a2a1e98e5ec9c840d"}, +] + +[package.extras] +tests = ["Sphinx", "doubles", "flake8", "flake8-quotes", "gevent", "mock", "pytest", "pytest-cov", "pytest-mock", "six (>=1.10.0,<2.0)", "sphinx_rtd_theme", "tornado"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pay_api" +version = "0.0.0" +description = "A short description of the project" +optional = false +python-versions = ">=3.12" +files = [] +develop = false + +[package.dependencies] +cattrs = "*" +croniter = "*" +cryptography = "*" +dpath = "*" +Flask = "*" +Flask-Caching = "*" +Flask-Cors = "*" +flask-jwt-oidc = "*" +flask-marshmallow = "*" +Flask-Migrate = "*" +Flask-Moment = "*" +Flask-Script = "*" +Flask-SQLAlchemy = "*" +google-auth = "2.28.1" +google-cloud-pubsub = "2.20.0" +gunicorn = "*" +holidays = "0.37" +itsdangerous = "*" +jaeger-client = "*" +Jinja2 = "*" +jsonschema = "4.17.3" +launchdarkly-server-sdk = "*" +marshmallow-sqlalchemy = "*" +psycopg2-binary = "*" +pyhumps = "*" +python-dotenv = "*" +requests = "*" +sentry-sdk = {version = "*", extras = ["flask"]} +sqlalchemy = "*" +sqlalchemy_utils = "*" +Werkzeug = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-pay.git" +reference = "feature-queue-python-upgrade" +resolved_reference = "06673215d07ac896fdc84e134f424de4d89c87b5" +subdirectory = "pay-api" + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "pg8000" +version = "1.30.5" +description = "PostgreSQL interface library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pg8000-1.30.5-py3-none-any.whl", hash = "sha256:1abf18da652b0ad8e9cbfe57ed841c350b5330c33d8151303555db1fe5ce57f8"}, + {file = "pg8000-1.30.5.tar.gz", hash = "sha256:072f7ad00cd723695cb2e9fc02c1dfb84c781455e97b8de6f4c4281eea08078c"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.2" +scramp = ">=1.4.4" + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "proto-plus" +version = "1.23.0" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.23.0.tar.gz", hash = "sha256:89075171ef11988b3fa157f5dbd8b9cf09d65fffee97e29ce403cd8defba19d2"}, + {file = "proto_plus-1.23.0-py3-none-any.whl", hash = "sha256:a829c79e619e1cf632de091013a4173deed13a55f326ef84f05af6f50ff4c82c"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + +[[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 = "pyasn1" +version = "0.5.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pyhamcrest" +version = "2.1.0" +description = "Hamcrest framework for matcher objects" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyhamcrest-2.1.0-py3-none-any.whl", hash = "sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587"}, + {file = "pyhamcrest-2.1.0.tar.gz", hash = "sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c"}, +] + +[package.extras] +dev = ["black", "doc2dash", "flake8", "pyhamcrest[docs,tests]", "pytest-mypy", "towncrier", "tox", "tox-asdf", "twine"] +docs = ["alabaster (>=0.7,<1.0)", "sphinx (>=4.0,<5.0)"] +tests = ["coverage[toml]", "dataclasses", "mypy (!=0.940)", "pytest (>=5.0)", "pytest-mypy-plugins", "pytest-sugar", "pytest-xdist", "pyyaml", "types-dataclasses", "types-mock"] +tests-numpy = ["numpy", "pyhamcrest[tests]"] + +[[package]] +name = "pyhumps" +version = "3.8.0" +description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" +optional = false +python-versions = "*" +files = [ + {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, + {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, +] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint-flask" +version = "0.6" +description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" +optional = false +python-versions = "*" +files = [ + {file = "pylint-flask-0.6.tar.gz", hash = "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517"}, +] + +[package.dependencies] +pylint-plugin-utils = ">=0.2.1" + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + +[[package]] +name = "pyparsing" +version = "3.1.2" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyrfc3339" +version = "1.1" +description = "Generate and parse RFC 3339 timestamps" +optional = false +python-versions = "*" +files = [ + {file = "pyRFC3339-1.1-py2.py3-none-any.whl", hash = "sha256:67196cb83b470709c580bb4738b83165e67c6cc60e1f2e4f286cfcb402a926f4"}, + {file = "pyRFC3339-1.1.tar.gz", hash = "sha256:81b8cbe1519cdb79bed04910dd6fa4e181faf8c88dff1e1b987b5f7ab23a5b1a"}, +] + +[package.dependencies] +pytz = "*" + +[[package]] +name = "pyrsistent" +version = "0.20.0" +description = "Persistent/Functional/Immutable data structures" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, + {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, + {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, + {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, + {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, + {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, + {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, +] + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.12.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, +] + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +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.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-futures" +version = "1.0.1" +description = "Asynchronous Python HTTP for Humans." +optional = false +python-versions = "*" +files = [ + {file = "requests-futures-1.0.1.tar.gz", hash = "sha256:f55a4ef80070e2858e7d1e73123d2bfaeaf25b93fd34384d8ddf148e2b676373"}, + {file = "requests_futures-1.0.1-py2.py3-none-any.whl", hash = "sha256:4a2f5472e9911a79532137d156aa937cd9cd90fec55677f71b2976d1f7a66d38"}, +] + +[package.dependencies] +requests = ">=1.2.0" + +[package.extras] +dev = ["black (>=22.3.0)", "build (>=0.7.0)", "isort (>=5.11.4)", "pyflakes (>=2.2.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "pytest-network (>=0.0.1)", "readme-renderer[rst] (>=26.0)", "twine (>=3.4.2)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "sbc_common_components" +version = "0.0.0" +description = "" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +flask = "*" +flask-jwt-oidc = ">=0.1.5" +Flask-OpenTracing = "1.1.0" +Flask-SQLAlchemy = "*" +jaeger-client = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-common-components.git" +reference = "HEAD" +resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +subdirectory = "python" + +[[package]] +name = "scramp" +version = "1.4.4" +description = "An implementation of the SCRAM protocol." +optional = false +python-versions = ">=3.7" +files = [ + {file = "scramp-1.4.4-py3-none-any.whl", hash = "sha256:b142312df7c2977241d951318b7ee923d6b7a4f75ba0f05b621ece1ed616faa3"}, + {file = "scramp-1.4.4.tar.gz", hash = "sha256:b7022a140040f33cf863ab2657917ed05287a807b917950489b89b9f685d59bc"}, +] + +[package.dependencies] +asn1crypto = ">=1.5.1" + +[[package]] +name = "semver" +version = "3.0.2" +description = "Python helper for Semantic Versioning (https://semver.org)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "semver-3.0.2-py3-none-any.whl", hash = "sha256:b1ea4686fe70b981f85359eda33199d60c53964284e0cfb4977d243e37cf4bf4"}, + {file = "semver-3.0.2.tar.gz", hash = "sha256:6253adb39c70f6e51afed2fa7152bcd414c411286088fb4b9effb133885ab4cc"}, +] + +[[package]] +name = "sentry-sdk" +version = "1.42.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"}, + {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"}, +] + +[package.dependencies] +blinker = {version = ">=1.1", optional = true, markers = "extra == \"flask\""} +certifi = "*" +flask = {version = ">=0.11", optional = true, markers = "extra == \"flask\""} +markupsafe = {version = "*", optional = true, markers = "extra == \"flask\""} +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "69.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "simple-cloudevent" +version = "0.0.2" +description = "A short description of the project" +optional = false +python-versions = ">=3.8" +files = [] +develop = false + +[package.dependencies] +strict-rfc3339 = "*" + +[package.source] +type = "git" +url = "https://github.com/daxiom/simple-cloudevent.py.git" +reference = "HEAD" +resolved_reference = "447cabb988202206ac69e71177d7cd11b6c0b002" + +[[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 = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.28" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "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\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlalchemy-utils" +version = "0.41.1" +description = "Various utility functions for SQLAlchemy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "SQLAlchemy-Utils-0.41.1.tar.gz", hash = "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74"}, + {file = "SQLAlchemy_Utils-0.41.1-py3-none-any.whl", hash = "sha256:6c96b0768ea3f15c0dc56b363d386138c562752b84f647fb8d31a2223aaab801"}, +] + +[package.dependencies] +SQLAlchemy = ">=1.3" + +[package.extras] +arrow = ["arrow (>=0.3.4)"] +babel = ["Babel (>=1.3)"] +color = ["colour (>=0.0.4)"] +encrypted = ["cryptography (>=0.6)"] +intervals = ["intervals (>=0.7.1)"] +password = ["passlib (>=1.6,<2.0)"] +pendulum = ["pendulum (>=2.0.5)"] +phone = ["phonenumbers (>=5.9.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +timezone = ["python-dateutil"] +url = ["furl (>=0.4.1)"] + +[[package]] +name = "strict-rfc3339" +version = "0.7" +description = "Strict, simple, lightweight RFC3339 functions" +optional = false +python-versions = "*" +files = [ + {file = "strict-rfc3339-0.7.tar.gz", hash = "sha256:5cad17bedfc3af57b399db0fed32771f18fc54bbd917e85546088607ac5e1277"}, +] + +[[package]] +name = "threadloop" +version = "1.0.2" +description = "Tornado IOLoop Backed Concurrent Futures" +optional = false +python-versions = "*" +files = [ + {file = "threadloop-1.0.2-py2-none-any.whl", hash = "sha256:5c90dbefab6ffbdba26afb4829d2a9df8275d13ac7dc58dccb0e279992679599"}, + {file = "threadloop-1.0.2.tar.gz", hash = "sha256:8b180aac31013de13c2ad5c834819771992d350267bddb854613ae77ef571944"}, +] + +[package.dependencies] +tornado = "*" + +[[package]] +name = "thrift" +version = "0.16.0" +description = "Python bindings for the Apache Thrift RPC system" +optional = false +python-versions = "*" +files = [ + {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, +] + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[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)"] + +[[package]] +name = "wtforms" +version = "3.1.2" +description = "Form validation and rendering for Python web development." +optional = false +python-versions = ">=3.8" +files = [ + {file = "wtforms-3.1.2-py3-none-any.whl", hash = "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07"}, + {file = "wtforms-3.1.2.tar.gz", hash = "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9"}, +] + +[package.dependencies] +markupsafe = "*" + +[package.extras] +email = ["email-validator"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "feabc25da8baaa7b9cefbf4555e8293a347a3d8a44c4c02f6ceb3c0839655256" diff --git a/pay-admin/pyproject.toml b/pay-admin/pyproject.toml new file mode 100644 index 000000000..24e846151 --- /dev/null +++ b/pay-admin/pyproject.toml @@ -0,0 +1,55 @@ +[tool.poetry] +name = "pay-admin" +version = "0.1.0" +description = "" +authors = ["Travis Semple "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +gunicorn = "^21.2.0" +flask = "^3.0.2" +flask-admin = "^1.6.1" +flask-oidc = "^2.1.1" +flask-sqlalchemy = "^3.1.1" +httplib2 = "^0.22.0" +psycopg2-binary = "^2.9.9" +python-dotenv = "^1.0.1" +requests = "^2.31.0" +requests-futures = "^1.0.1" +wtforms = "^3.1.2" +werkzeug = "^3.0.1" +sqlalchemy = "^2.0.28" +itsdangerous = "^2.1.2" +jinja2 = "^3.1.3" +pay-api = {git = "https://github.com/bcgov/sbc-pay.git", rev = "feature-queue-python-upgrade", subdirectory = "pay-api"} +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +pg8000 = "^1.30.5" + + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pytest-mock = "^3.12.0" +requests = "^2.31.0" +pyhamcrest = "^2.1.0" +pytest-cov = "^4.1.0" +freezegun = "^1.4.0" +flake8 = "^7.0.0" +flake8-blind-except = "^0.2.1" +flake8-debugger = "^4.1.2" +flake8-docstrings = "^1.7.0" +flake8-isort = "^6.1.1" +flake8-quotes = "^3.4.0" +pep8-naming = "^0.13.3" +autopep8 = "^2.0.4" +coverage = "^7.4.3" +pylint = "^3.1.0" +pylint-flask = "^0.6" +pydocstyle = "^6.3.0" +isort = "^5.13.2" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/pay-admin/requirements.txt b/pay-admin/requirements.txt deleted file mode 100644 index 2ee29fe87..000000000 --- a/pay-admin/requirements.txt +++ /dev/null @@ -1,30 +0,0 @@ -Flask-Admin==1.6.0 -Flask-SQLAlchemy==2.5.1 -Flask==1.1.2 -Jinja2==3.0.3 -MarkupSafe==2.1.1 -SQLAlchemy==1.3.24 -WTForms==3.0.1 -Werkzeug==1.0.1 -certifi==2023.7.22 -charset-normalizer==2.0.12 -click==8.1.3 -flask-oidc==1.4.0 -gunicorn==20.1.0 -httplib2==0.20.4 -idna==3.7 -itsdangerous==2.0.1 -oauth2client==4.1.3 -psycopg2-binary==2.9.3 -pyasn1-modules==0.2.8 -pyasn1==0.4.8 -pyparsing==3.0.8 -python-dotenv==0.20.0 -requests-futures==1.0.0 -requests==2.31.0 -rsa==4.8 -six==1.16.0 -urllib3==1.26.17 --e git+https://github.com/bcgov/sbc-pay.git@main#egg=pay-api&subdirectory=pay-api --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python -git+https://github.com/daxiom/simple-cloudevent.py.git diff --git a/pay-admin/requirements/dev.txt b/pay-admin/requirements/dev.txt deleted file mode 100644 index 51d7a0057..000000000 --- a/pay-admin/requirements/dev.txt +++ /dev/null @@ -1,24 +0,0 @@ --r prod.txt - -# Testing -pytest -pytest-mock -requests -pyhamcrest -pytest-cov -FreezeGun - -# Lint and code style -flake8==5.0.4 -flake8-blind-except -flake8-debugger -flake8-docstrings -flake8-isort -flake8-quotes -pep8-naming -autopep8 -coverage -pylint -pylint-flask -pydocstyle -isort diff --git a/pay-admin/requirements/prod.txt b/pay-admin/requirements/prod.txt deleted file mode 100644 index 58a9a14bd..000000000 --- a/pay-admin/requirements/prod.txt +++ /dev/null @@ -1,15 +0,0 @@ -gunicorn -Flask -Flask-Admin -Flask-OIDC -Flask-SQLAlchemy -httplib2 -psycopg2-binary -python-dotenv -requests -requests-futures -wtforms -Werkzeug<2 -sqlalchemy<1.4 -itsdangerous==2.0.1 -Jinja2==3.0.3 \ No newline at end of file diff --git a/pay-admin/requirements/repo-libraries.txt b/pay-admin/requirements/repo-libraries.txt deleted file mode 100644 index 146c61f8c..000000000 --- a/pay-admin/requirements/repo-libraries.txt +++ /dev/null @@ -1,3 +0,0 @@ --e git+https://github.com/bcgov/sbc-pay.git@main#egg=pay-api&subdirectory=pay-api --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python -git+https://github.com/daxiom/simple-cloudevent.py.git diff --git a/pay-admin/secrets/keycloak.json b/pay-admin/secrets/keycloak.json index f1a775134..51a30edd9 100644 --- a/pay-admin/secrets/keycloak.json +++ b/pay-admin/secrets/keycloak.json @@ -1,10 +1,11 @@ { "web": { - "auth_uri": "https://dev.oidc.gov.bc.ca/auth/realms/fcf0kpqr/protocol/openid-connect/auth", + "auth_uri": "https://dev.loginproxy.gov.bc.ca/auth/admin/bcregistry/protocol/openid-connect/auth", "client_id": "pay-admin", "client_secret": "****", - "userinfo_uri": "https://dev.oidc.gov.bc.ca/auth/realms/fcf0kpqr/protocol/openid-connect/userinfo", - "token_uri": "https://dev.oidc.gov.bc.ca/auth/realms/fcf0kpqr/protocol/openid-connect/token", - "token_introspection_uri": "https://dev.oidc.gov.bc.ca/auth/realms/fcf0kpqr/protocol/openid-connect/token/introspect" + "issuer": "https://dev.loginproxy.gov.bc.ca/auth/realms/bcregistry", + "userinfo_uri": "https://dev.loginproxy.gov.bc.ca/auth/admin/bcregistry/protocol/openid-connect/userinfo", + "token_uri": "https://dev.loginproxy.gov.bc.ca/auth/admin/bcregistry/protocol/openid-connect/token", + "token_introspection_uri": "https://dev.loginproxy.gov.bc.ca/auth/admin/bcregistry/protocol/openid-connect/token/introspect" } } diff --git a/pay-admin/setup.cfg b/pay-admin/setup.cfg index 50d2c7190..fd41bb9f4 100755 --- a/pay-admin/setup.cfg +++ b/pay-admin/setup.cfg @@ -17,7 +17,7 @@ keywords = [options] zip_safe = True -python_requires = >=3.6 +python_requires = >=3.12 include_package_data = True packages = find: diff --git a/pay-admin/tests/conftest.py b/pay-admin/tests/conftest.py index beda94298..50235bdaa 100644 --- a/pay-admin/tests/conftest.py +++ b/pay-admin/tests/conftest.py @@ -4,26 +4,19 @@ """ import pytest +from flask_sqlalchemy import SQLAlchemy -@pytest.fixture(scope='function') -def clean_db(): - """Clean DB.""" - from flask_sqlalchemy import SQLAlchemy - - from admin import create_app - from admin.keycloak import Keycloak - from tests.fake_oidc import FakeOidc - - Keycloak._oidc = FakeOidc() - app = create_app(run_mode='testing') - - db = SQLAlchemy(app) - - return db +from admin import create_app +from admin.keycloak import Keycloak +from tests.fake_oidc import FakeOidc @pytest.fixture(scope='function') -def db(clean_db): +def db(): """DB.""" - yield clean_db - clean_db.session.close() + Keycloak._oidc = FakeOidc() # pylint-disable=protected-access + app = create_app(run_mode='testing') + with app.app_context(): + _db = SQLAlchemy() + _db.app = app + return _db diff --git a/pay-api/Dockerfile b/pay-api/Dockerfile index 7ec36797c..cf3e9d235 100644 --- a/pay-api/Dockerfile +++ b/pay-api/Dockerfile @@ -1,33 +1,88 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye as development_build ARG VCS_REF="missing" ARG BUILD_DATE="missing" ENV VCS_REF=${VCS_REF} ENV BUILD_DATE=${BUILD_DATE} +ENV PORT=8080 LABEL org.label-schema.vcs-ref=${VCS_REF} \ org.label-schema.build-date=${BUILD_DATE} USER root -# Create working directory -RUN mkdir /opt/app-root && chmod 755 /opt/app-root -WORKDIR /opt/app-root +LABEL maintainer="travissemple" +LABEL vendor="BCROS" -# Install the requirements -COPY ./requirements.txt . +ARG APP_ENV \ + # Needed for fixing permissions of files created by Docker: + UID=1000 \ + GID=1000 -RUN pip install --upgrade pip -RUN pip install --no-cache-dir -r requirements.txt +ENV APP_ENV=${APP_ENV} \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_DEFAULT_TIMEOUT=100 \ + PIP_ROOT_USER_ACTION=ignore \ + # poetry: + POETRY_VERSION=1.8.3 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + POETRY_HOME='/usr/local' -COPY . . +SHELL ["/bin/bash", "-eo", "pipefail", "-c"] -RUN pip install . +RUN apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y \ + bash \ + brotli \ + build-essential \ + curl \ + gettext \ + git \ + libpq-dev \ + wait-for-it \ + && curl -sSL 'https://install.python-poetry.org' | python - \ + && poetry --version \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* -USER 1001 +WORKDIR /code -# Set Python path -ENV PYTHONPATH=/opt/app-root/src +RUN groupadd -g "${GID}" -r web \ + && useradd -d '/code' -g web -l -r -u "${UID}" web \ + && chown web:web -R '/code' -ENTRYPOINT ["bash", "docker-entrypoint.sh"] +# Copy only requirements, to cache them in docker layer +COPY --chown=web:web ./poetry.lock ./pyproject.toml /code/ + +COPY --chown=web:web ./src /code/src +COPY --chown=web:web ./README.md /code + +# Project initialization: +RUN --mount=type=cache,target="$POETRY_CACHE_DIR" \ + echo "$APP_ENV" \ + && poetry version \ + # Install deps: + && poetry run pip install -U pip \ + && poetry install \ + $(if [ -z ${APP_ENV+x} ] | [ "$APP_ENV" = 'production' ]; then echo '--only main'; fi) \ + --no-interaction --no-ansi + +# Running as non-root user: +USER web + +# The following stage is only for production: +FROM development_build AS production_build +COPY --chown=web:web . /code + +CMD gunicorn --bind 0.0.0.0:${PORT} --config /code/gunicorn_config.py wsgi:app diff --git a/pay-api/Makefile b/pay-api/Makefile index 7c2976754..03af18905 100755 --- a/pay-api/Makefile +++ b/pay-api/Makefile @@ -12,7 +12,7 @@ DOCKER_NAME:=pay-api ################################################################################# # COMMANDS -- Setup # ################################################################################# -setup: install install-dev ## Setup the project +setup: install ## Setup the project clean: clean-build clean-pyc clean-test ## Clean the project rm -rf venv/ @@ -36,25 +36,9 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -build-req: clean ## Upgrade requirements - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements/prod.txt ;\ - pip freeze | sort > requirements.txt ;\ - cat requirements/repo-libraries.txt >> requirements.txt ;\ - pip install -Ur requirements/repo-libraries.txt - -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements.txt - -install-dev: ## Install local application - . venv/bin/activate ; \ - pip install -Ur requirements/dev.txt; \ - pip install -e . +install: clean + pip install poetry ;\ + poetry install ################################################################################# # COMMANDS - CI # @@ -62,15 +46,15 @@ install-dev: ## Install local application ci: lint flake8 test ## CI flow pylint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg src/$(PROJECT_NAME) + poetry run pylint --rcfile=setup.cfg src/$(PROJECT_NAME) flake8: ## Linting with flake8 - . venv/bin/activate && flake8 src/$(PROJECT_NAME) tests + poetry run flake8 src/$(PROJECT_NAME) tests lint: pylint flake8 ## run all lint type scripts test: ## Unit testing - . venv/bin/activate && pytest + poetry run pytest mac-cov: test ## Run the coverage report and display in a browser window (mac) @open -a "Google Chrome" htmlcov/index.html @@ -131,7 +115,7 @@ tag: push ## tag image # COMMANDS - Local # ################################################################################# run: ## Run the project in local - . venv/bin/activate && python -m flask run -p 5000 + poetry run flask run -p 5000 ################################################################################# # Self Documenting Commands # diff --git a/pay-api/devops/gcp/clouddeploy-targets.yaml b/pay-api/devops/gcp/clouddeploy-targets.yaml new file mode 100644 index 000000000..578fdbcd0 --- /dev/null +++ b/pay-api/devops/gcp/clouddeploy-targets.yaml @@ -0,0 +1,104 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: dev +description: Dev Environment +deployParameters: + deploy-env: "development" + deploy-project-id: "gtksf3-dev" + service-name: "pay-api-dev" + container-name: "pay-api-dev" + app-env: "dev" + cloudsql-instances: "gtksf3-dev:northamerica-northeast1:pay-db-dev" + service-account: "sa-api@gtksf3-dev.iam.gserviceaccount.com" +run: + location: projects/gtksf3-dev/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: test +description: Test Environment +deployParameters: + deploy-env: "development" + deploy-project-id: "gtksf3-test" + service-name: "pay-api-test" + container-name: "pay-api-test" + app-env: "test" + cloudsql-instances: "gtksf3-test:northamerica-northeast1:pay-db-test" + service-account: "sa-api@gtksf3-test.iam.gserviceaccount.com" +run: + location: projects/gtksf3-test/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: sandbox +description: Sandbox Environment +requireApproval: true +deployParameters: + deploy-env: "production" + deploy-project-id: "gtksf3-tools" + service-name: "pay-api-sandbox" + container-name: "pay-api-sandbox" + app-env: "sandbox" + cloudsql-instances: "gtksf3-tools:northamerica-northeast1:pay-db-sandbox" + service-account: "sa-api@gtksf3-tools.iam.gserviceaccount.com" + max-scale: "50" + container-concurrency: "20" + container-port: "8080" + resources-cpu: 4000m + resources-memory: 8Gi +run: + location: projects/gtksf3-tools/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: prod +description: Production Environment +requireApproval: true +deployParameters: + deploy-env: "production" + deploy-project-id: "gtksf3-prod" + service-name: "pay-api-prod" + container-name: "pay-api-prod" + app-env: "production" + cloudsql-instances: "gtksf3-prod:northamerica-northeast1:pay-db-prod" + service-account: "sa-api@gtksf3-prod.iam.gserviceaccount.com" + max-scale: "50" + container-concurrency: "20" + container-port: "8080" + resources-cpu: 4000m + resources-memory: 8Gi +run: + location: projects/gtksf3-prod/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' \ No newline at end of file diff --git a/pay-api/devops/vaults.gcp.env b/pay-api/devops/vaults.gcp.env new file mode 100644 index 000000000..e40127bab --- /dev/null +++ b/pay-api/devops/vaults.gcp.env @@ -0,0 +1,59 @@ +PAY_LD_SDK_KEY="op://launchdarkly/$APP_ENV/pay/PAY_LD_SDK_KEY" +DATABASE_NAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_NAME" +DATABASE_PASSWORD="op://database/$APP_ENV/pay-db-gcp/DATABASE_PASSWORD" +DATABASE_PORT="op://database/$APP_ENV/pay-db-gcp/DATABASE_PORT" +DATABASE_UNIX_SOCKET="op://database/$APP_ENV/pay-db-gcp/DATABASE_UNIX_SOCKET" +DATABASE_USERNAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_USERNAME" +JWT_OIDC_AUDIENCE="op://keycloak/$APP_ENV/account-services-account/ACCOUNT_SERVICES_SERVICE_ACCOUNT_CLIENT_ID" +JWT_OIDC_JWKS_CACHE_TIMEOUT="op://keycloak/$APP_ENV/jwt-base/JWT_OIDC_JWKS_CACHE_TIMEOUT" +JWT_OIDC_WELL_KNOWN_CONFIG="op://keycloak/$APP_ENV/jwt-base/JWT_OIDC_WELL_KNOWN_CONFIG" +JWT_OIDC_ISSUER="op://keycloak/$APP_ENV/jwt-base/JWT_OIDC_ISSUER" +JWT_OIDC_CACHING_ENABLED="op://keycloak/$APP_ENV/jwt-base/JWT_OIDC_CACHING_ENABLED" +JWT_OIDC_ALGORITHMS="op://keycloak/$APP_ENV/jwt-base/JWT_OIDC_ALGORITHMS" +SBC_AUTH_ADMIN_CLIENT_ID="op://keycloak/$APP_ENV/sbc-auth-admin/SBC_AUTH_ADMIN_CLIENT_ID" +SBC_AUTH_ADMIN_CLIENT_SECRET="op://keycloak/$APP_ENV/sbc-auth-admin/SBC_AUTH_ADMIN_CLIENT_SECRET" +CFS_BASE_URL="op://payment-external-services/$APP_ENV/cfs/CFS_BASE_URL" +CFS_CLIENT_ID="op://payment-external-services/$APP_ENV/cfs/CFS_CLIENT_ID" +CFS_CLIENT_SECRET="op://payment-external-services/$APP_ENV/cfs/CFS_CLIENT_SECRET" +PAYBC_PORTAL_URL="op://payment-external-services/$APP_ENV/cfs/PAYBC_PORTAL_URL" +CONNECT_TIMEOUT="op://payment-external-services/$APP_ENV/cfs/CONNECT_TIMEOUT" +CFS_GENERATE_RANDOM_INVOICE_NUMBER="op://payment-external-services/$APP_ENV/cfs/CFS_GENERATE_RANDOM_INVOICE_NUMBER" +CFS_ACCOUNT_DESCRIPTION="op://payment-external-services/$APP_ENV/cfs/CFS_ACCOUNT_DESCRIPTION" +CFS_INVOICE_PREFIX="op://payment-external-services/$APP_ENV/cfs/CFS_INVOICE_PREFIX" +CFS_RECEIPT_PREFIX="op://payment-external-services/$APP_ENV/cfs/CFS_RECEIPT_PREFIX" +CFS_PARTY_PREFIX="op://payment-external-services/$APP_ENV/cfs/CFS_PARTY_PREFIX" +EFT_INVOICE_PREFIX="op://payment-external-services/$APP_ENV/eft/EFT_INVOICE_PREFIX" +PAYBC_DIRECT_PAY_REF_NUMBER="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_REF_NUMBER" +PAYBC_DIRECT_PAY_API_KEY="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_API_KEY" +PAYBC_DIRECT_PAY_PORTAL_URL="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_PORTAL_URL" +PAYBC_DIRECT_PAY_BASE_URL="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_BASE_URL" +PAYBC_DIRECT_PAY_CLIENT_ID="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_CLIENT_ID" +PAYBC_DIRECT_PAY_CLIENT_SECRET="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_CLIENT_SECRET" +PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL" +AUDIENCE="op://gcp-queue/$APP_ENV/base/AUDIENCE" +GCP_AUTH_KEY="op://gcp-queue/$APP_ENV/base/GCP_AUTH_KEY" +PUBLISHER_AUDIENCE="op://gcp-queue/$APP_ENV/base/PUBLISHER_AUDIENCE" +ACCOUNT_MAILER_TOPIC="op://gcp-queue/$APP_ENV/topics/ACCOUNT_MAILER_TOPIC" +EVENT_LISTENER_TOPIC="op://gcp-queue/$APP_ENV/topics/EVENT_LISTENER_TOPIC" +NAMEX_PAY_TOPIC="op://gcp-queue/$APP_ENV/topics/NAMEX_PAY_TOPIC" +BUSINESS_PAY_TOPIC="op://gcp-queue/$APP_ENV/topics/BUSINESS_PAY_TOPIC" +AUTH_API_URL="op://API/$APP_ENV/auth-api/AUTH_API_URL" +AUTH_API_VERSION="op://API/$APP_ENV/auth-api/AUTH_API_VERSION" +BCOL_API_URL="op://API/$APP_ENV/bcol-api/BCOL_API_URL" +BCOL_API_VERSION="op://API/$APP_ENV/bcol-api/BCOL_API_VERSION" +REPORT_API_URL="op://API/$APP_ENV/report-api/REPORT_API_URL" +REPORT_API_VERSION="op://API/$APP_ENV/report-api/REPORT_API_VERSION" +SENTRY_ENABLE="op://sentry/$APP_ENV/relationship-api/SENTRY_ENABLE" +SENTRY_DSN="op://sentry/$APP_ENV/relationship-api/SENTRY_DSN" +DISABLE_VALID_REDIRECT_URLS="op://relationship/$APP_ENV/pay-api/DISABLE_VALID_REDIRECT_URLS" +VALID_REDIRECT_URLS="op://relationship/$APP_ENV/pay-api/VALID_REDIRECT_URLS" +TRANSACTION_REPORT_DEFAULT_TOTAL="op://relationship/$APP_ENV/pay-api/TRANSACTION_REPORT_DEFAULT_TOTAL" +ROUTING_SLIP_DEFAULT_TOTAL="op://relationship/$APP_ENV/pay-api/ROUTING_SLIP_DEFAULT_TOTAL" +PAD_CONFIRMATION_PERIOD_IN_DAYS="op://relationship/$APP_ENV/pay-api/PAD_CONFIRMATION_PERIOD_IN_DAYS" +LEGISLATIVE_TIMEZONE="op://relationship/$APP_ENV/pay-api/LEGISLATIVE_TIMEZONE" +BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS="op://relationship/$APP_ENV/pay-api/BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS" +MASK_LEN="op://relationship/$APP_ENV/pay-api/MASK_LEN" +DISABLE_ACTIVITY_LOGS="op://relationship/$APP_ENV/pay-api/DISABLE_ACTIVITY_LOGS" +ACCOUNT_SECRET_KEY="op://relationship/$APP_ENV/pay-api/ACCOUNT_SECRET_KEY" +OUTSTANDING_TRANSACTION_DAYS="op://relationship/$APP_ENV/pay-api/OUTSTANDING_TRANSACTION_DAYS" +ALLOW_LEGACY_ROUTING_SLIPS="op://relationship/$APP_ENV/pay-api/ALLOW_LEGACY_ROUTING_SLIPS" \ No newline at end of file diff --git a/pay-api/docker-entrypoint.sh b/pay-api/docker-entrypoint.sh deleted file mode 100755 index 0cf2a64b2..000000000 --- a/pay-api/docker-entrypoint.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -echo 'starting application' -gunicorn --bind 0.0.0.0:8080 --config /opt/app-root/gunicorn_config.py wsgi:application diff --git a/pay-api/docs/dotenv_template b/pay-api/docs/dotenv_template index b58016de8..af1dbf20e 100644 --- a/pay-api/docs/dotenv_template +++ b/pay-api/docs/dotenv_template @@ -43,7 +43,7 @@ JWT_OIDC_AUDIENCE= JWT_OIDC_CLIENT_SECRET= JWT_OIDC_JWKS_CACHE_TIMEOUT= -KEYCLOAK_ADMIN_CLIENTID=' +KEYCLOAK_ADMIN_CLIENTID= KEYCLOAK_ADMIN_SECRET= KEYCLOAK_AUTH_AUDIENCE= KEYCLOAK_AUTH_CLIENT_SECRET= diff --git a/pay-api/gunicorn_config.py b/pay-api/gunicorn_config.py index f37c7d48d..484760c33 100755 --- a/pay-api/gunicorn_config.py +++ b/pay-api/gunicorn_config.py @@ -24,6 +24,7 @@ threads = int(os.environ.get('GUNICORN_THREADS', '1')) # gunicorn default - 1 timeout = int(os.environ.get('GUNICORN_TIMEOUT', '100')) # gunicorn default - 30 keepalive = int(os.environ.get('GUNICORN_KEEPALIVE', '2')) # gunicorn default - 2 +# WHEN MIGRATING TO GCP - GUNICORN_THREADS = 8, GUNICORN_TIMEOUT = 0, GUNICORN_PROCESSES = 1 forwarded_allow_ips = '*' # pylint: disable=invalid-name diff --git a/pay-api/manage.py b/pay-api/manage.py index ae4d9df78..7f786c35b 100755 --- a/pay-api/manage.py +++ b/pay-api/manage.py @@ -16,8 +16,7 @@ """ import logging -from flask_migrate import Migrate, MigrateCommand -from flask_script import Manager # class for handling a set of commands +from flask_migrate import Migrate # models included so that migrate can build the database migrations from pay_api import models # pylint: disable=unused-import @@ -27,10 +26,6 @@ APP = create_app(run_mode='migration') MIGRATE = Migrate(APP, db) -MANAGER = Manager(APP) - -MANAGER.add_command('db', MigrateCommand) if __name__ == '__main__': logging.log(logging.INFO, 'Running the Manager') - MANAGER.run() diff --git a/pay-api/migrations/README.md b/pay-api/migrations/README.md index 88c686dc4..a55983d6b 100644 --- a/pay-api/migrations/README.md +++ b/pay-api/migrations/README.md @@ -2,6 +2,6 @@ Run `python manage.py db migrate` -If you are updating a large table (i.e. invoices, invoice_references, etc.) add `op.execute("set statement_timeout=20000;")` to the top of your new migration scripts for upgrade/downgrade. This will prevent the deployment from causing errors in prod when it takes too long to complete (> 20 seconds). +If you are updating a large table (i.e. invoices, invoice_references, etc.) add `op.execute(op.text("set statement_timeout=20000;")` to the top of your new migration scripts for upgrade/downgrade. This will prevent the deployment from causing errors in prod when it takes too long to complete (> 20 seconds).) _Note: If it takes too long this will cause the prehook to fail in the deployment so you will need to watch it and redeploy at a less busy time if necessary_ diff --git a/pay-api/migrations/env.py b/pay-api/migrations/env.py index 911e5fab6..e6603a9b4 100644 --- a/pay-api/migrations/env.py +++ b/pay-api/migrations/env.py @@ -80,7 +80,6 @@ def process_revision_directives(context, revision, directives): context.configure( connection=connection, target_metadata=target_metadata, - compare_type=True, process_revision_directives=process_revision_directives, **current_app.extensions['migrate'].configure_args ) diff --git a/pay-api/migrations/versions/03b2c7caed21_populate_receipt_number_payments.py b/pay-api/migrations/versions/03b2c7caed21_populate_receipt_number_payments.py index 3dbdc79c1..85cb2f9d4 100644 --- a/pay-api/migrations/versions/03b2c7caed21_populate_receipt_number_payments.py +++ b/pay-api/migrations/versions/03b2c7caed21_populate_receipt_number_payments.py @@ -7,6 +7,7 @@ """ from alembic import op +from sqlalchemy import text # revision identifiers, used by Alembic. revision = '03b2c7caed21' @@ -17,14 +18,14 @@ def upgrade(): conn = op.get_bind() - res = conn.execute(f"select id, invoice_number from payments where receipt_number is null;") + res = conn.execute(text(f"select id, invoice_number from payments where receipt_number is null;")) results = res.fetchall() for result in results: pay_id = result[0] invoice_number = result[1] - res = conn.execute(f"select r.receipt_number from receipts r left join invoice_references ir " + res = conn.execute(text(f"select r.receipt_number from receipts r left join invoice_references ir " f"on ir.invoice_id=r.invoice_id where ir.status_code='COMPLETED' " - f"and invoice_number='{invoice_number}'") + f"and invoice_number='{invoice_number}'")) receipt_number_result = res.fetchall() if receipt_number_result: receipt_number = receipt_number_result[0][0] diff --git a/pay-api/migrations/versions/1283463d3b4a_new_payment_method_tables.py b/pay-api/migrations/versions/1283463d3b4a_new_payment_method_tables.py index 379c0c2f0..10443a569 100644 --- a/pay-api/migrations/versions/1283463d3b4a_new_payment_method_tables.py +++ b/pay-api/migrations/versions/1283463d3b4a_new_payment_method_tables.py @@ -101,8 +101,8 @@ def upgrade(): # Update invoice which have credit_account_id with matching details from cfs_account conn = op.get_bind() - res = conn.execute("select cfs.id, credit.id from cfs_account cfs, credit_payment_account credit " - "where credit.paybc_account=cfs.cfs_account and credit.account_id=cfs.account_id;") + res = conn.execute(sa.text("select cfs.id, credit.id from cfs_account cfs, credit_payment_account credit " + "where credit.paybc_account=cfs.cfs_account and credit.account_id=cfs.account_id;")) cfs_accounts = res.fetchall() for cfs_account in cfs_accounts: cfs_id = cfs_account[0] diff --git a/pay-api/migrations/versions/2023_02_23_d2d9864164d1_update_refund_payment_date_to_invoices.py b/pay-api/migrations/versions/2023_02_23_d2d9864164d1_update_refund_payment_date_to_invoices.py index dd5dc7f5e..0f9b2df54 100644 --- a/pay-api/migrations/versions/2023_02_23_d2d9864164d1_update_refund_payment_date_to_invoices.py +++ b/pay-api/migrations/versions/2023_02_23_d2d9864164d1_update_refund_payment_date_to_invoices.py @@ -6,6 +6,7 @@ """ from alembic import op +from sqlalchemy import text # revision identifiers, used by Alembic. @@ -21,29 +22,29 @@ def upgrade(): # 2. Update payment_date with invoice_id. op.execute("set statement_timeout=20000;") conn = op.get_bind() - res = conn.execute(f'select i.id as "id", p.payment_date as "date"\ + res = conn.execute(text(f'select i.id as "id", p.payment_date as "date"\ from invoice_references ir \ inner join invoices i on i.id = ir.invoice_id \ inner join payments p on p.invoice_number = ir.invoice_number \ - where p.payment_date IS NOT null AND i.payment_date IS null;') + where p.payment_date IS NOT null AND i.payment_date IS null;')) payments = res.fetchall() for payment in payments: invoice_id = payment[0] payment_date = payment[1] - op.execute(f"update invoices set payment_date=\'{payment_date}\' where id = {invoice_id}") + op.execute(text(f"update invoices set payment_date=\'{payment_date}\' where id = {invoice_id}")) # 1. Find all id and refund date from invoice table linking with refunds table. # 2. Update refund_date with invoice_id. - res = conn.execute(f'select i.id as "id", r.requested_date as "date"\ + res = conn.execute(text(f'select i.id as "id", r.requested_date as "date"\ from invoices i \ inner join refunds r on r.invoice_id = i.id\ where i.invoice_status_code = \'REFUNDED\'\ - and i.refund_date is null;') + and i.refund_date is null;')) refunds = res.fetchall() for refund in refunds: id = refund[0] refund_date = refund[1] - op.execute(f"update invoices set refund_date=\'{refund_date}\' where id = {id}") + op.execute(text(f"update invoices set refund_date=\'{refund_date}\' where id = {id}")) def downgrade(): diff --git a/pay-api/migrations/versions/2023_09_28_456234145e5e_eft_processing.py b/pay-api/migrations/versions/2023_09_28_456234145e5e_eft_processing.py index 0bd18d990..8ad95bafd 100644 --- a/pay-api/migrations/versions/2023_09_28_456234145e5e_eft_processing.py +++ b/pay-api/migrations/versions/2023_09_28_456234145e5e_eft_processing.py @@ -56,7 +56,6 @@ def upgrade(): sa.Column('completed_on', sa.DateTime(), nullable=True), sa.Column('line_number', sa.Integer(), nullable=False), sa.Column('line_type', sa.String(20), nullable=False), - sa.Column('batch_number', sa.String(10), nullable=True), sa.Column('sequence_number', sa.String(3), nullable=True), sa.Column('jv_type', sa.String(1), nullable=True), sa.Column('jv_number', sa.String(10), nullable=True), diff --git a/pay-api/migrations/versions/2024_03_06_04b8a7bed74e_ejv_link_generic.py b/pay-api/migrations/versions/2024_03_06_04b8a7bed74e_ejv_link_generic.py new file mode 100644 index 000000000..eca30695c --- /dev/null +++ b/pay-api/migrations/versions/2024_03_06_04b8a7bed74e_ejv_link_generic.py @@ -0,0 +1,39 @@ +"""Rename ejv_invoice_links to ejv_links, alter invoice_id to link_id, removed foreign constraint, and added link_type column + +Revision ID: 04b8a7bed74e +Revises: bacb2b859d78 +Create Date: 2024-03-06 11:40:00.153387 + +""" +from alembic import op +import sqlalchemy as sa + +from pay_api.utils.enums import EJVLinkType + +# revision identifiers, used by Alembic. +revision = '04b8a7bed74e' +down_revision = 'bacb2b859d78' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column('ejv_invoice_links', sa.Column('link_type', sa.String(length=20), nullable=True)) + op.drop_constraint('ejv_invoice_links_invoice_id_fkey', 'ejv_invoice_links', type_='foreignkey') + op.drop_index(op.f('ix_ejv_invoice_links_invoice_id'), table_name='ejv_invoice_links') + op.alter_column('ejv_invoice_links', 'invoice_id', new_column_name='link_id', + existing_type=sa.Integer(), nullable=True) + op.rename_table('ejv_invoice_links', 'ejv_links') + # as there is no data for partial refunds yet. + op.execute(f"UPDATE ejv_links set link_type='{EJVLinkType.INVOICE.value}'") + + op.create_index('ix_ejv_links_link_type_link_id', 'ejv_links', ['link_type', 'link_id'], unique=False) + +def downgrade(): + op.drop_index('ix_ejv_links_link_type_link_id', table_name='ejv_links') + op.rename_table('ejv_links', 'ejv_invoice_links') + op.alter_column('ejv_invoice_links', 'link_id', new_column_name='invoice_id', + existing_type=sa.Integer(), nullable=False) + op.create_index(op.f('ix_ejv_invoice_links_invoice_id'), 'ejv_invoice_links', ['invoice_id'], unique=False) + op.drop_column('ejv_invoice_links', 'link_type') + op.create_foreign_key('ejv_invoice_links_invoice_id_fkey', 'ejv_invoice_links', 'invoices', ['invoice_id'], ['id']) diff --git a/pay-api/migrations/versions/2024_03_15_fb3ba97b603a_new_version_tables_.py b/pay-api/migrations/versions/2024_03_15_fb3ba97b603a_new_version_tables_.py new file mode 100644 index 000000000..5e4616332 --- /dev/null +++ b/pay-api/migrations/versions/2024_03_15_fb3ba97b603a_new_version_tables_.py @@ -0,0 +1,241 @@ +"""Migration for new history tables + +A special thanks to LEAR devs (Thor, Argus, Vysakh) for this migration and the history table implementation: +https://github.com/bcgov/lear/blob/feature-legal-name/legal-api/scripts/manual_db_scripts/legal_name_change/transfer_to_new_lear.sql + +Revision ID: fb3ba97b603a +Revises: 04b8a7bed74e +Create Date: 2024-03-15 15:22:53.140353 + +""" +from alembic import op +import sqlalchemy as sa +import sqlalchemy_utils +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'fb3ba97b603a' +down_revision = '04b8a7bed74e' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('account_fees', schema=None) as batch_op: + batch_op.add_column(sa.Column('version', sa.Integer(), nullable=False, server_default='1')) + + with op.batch_alter_table('cfs_accounts', schema=None) as batch_op: + batch_op.add_column(sa.Column('version', sa.Integer(), nullable=False, server_default='1')) + + with op.batch_alter_table('distribution_codes', schema=None) as batch_op: + batch_op.add_column(sa.Column('version', sa.Integer(), nullable=False, server_default='1')) + + with op.batch_alter_table('eft_short_names', schema=None) as batch_op: + batch_op.add_column(sa.Column('version', sa.Integer(), nullable=False, server_default='1')) + + with op.batch_alter_table('payment_accounts', schema=None) as batch_op: + batch_op.add_column(sa.Column('version', sa.Integer(), nullable=False, server_default='1')) + + with op.batch_alter_table('refunds_partial', schema=None) as batch_op: + batch_op.add_column(sa.Column('version', sa.Integer(), nullable=False, server_default='1')) + + op.create_table('eft_short_names_history', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('auth_account_id', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('created_on', sa.DateTime(), autoincrement=False, nullable=False), + sa.Column('short_name', sa.String(), autoincrement=False, nullable=False), + sa.Column('linked_by', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('linked_by_name', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('linked_on', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id', 'version'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('eft_short_names_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_eft_short_names_history_auth_account_id'), ['auth_account_id'], unique=False) + batch_op.create_index(batch_op.f('ix_eft_short_names_history_short_name'), ['short_name'], unique=False) + + op.create_table('payment_accounts_history', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('auth_account_id', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('name', sa.String(length=250), autoincrement=False, nullable=True), + sa.Column('branch_name', sa.String(length=250), autoincrement=False, nullable=True), + sa.Column('payment_method', sa.String(length=15), autoincrement=False, nullable=True), + sa.Column('bcol_user_id', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('bcol_account', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('statement_notification_enabled', sa.Boolean(), autoincrement=False, nullable=True), + sa.Column('credit', sa.Numeric(precision=19, scale=2), autoincrement=False, nullable=True), + sa.Column('billable', sa.Boolean(), autoincrement=False, nullable=True), + sa.Column('eft_enable', sa.Boolean(), autoincrement=False, nullable=False), + sa.Column('pad_activation_date', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('pad_tos_accepted_date', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('pad_tos_accepted_by', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['payment_method'], ['payment_methods.code'], ), + sa.PrimaryKeyConstraint('id', 'version'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('payment_accounts_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_payment_accounts_history_auth_account_id'), ['auth_account_id'], unique=False) + batch_op.create_index(batch_op.f('ix_payment_accounts_history_bcol_account'), ['bcol_account'], unique=False) + batch_op.create_index(batch_op.f('ix_payment_accounts_history_bcol_user_id'), ['bcol_user_id'], unique=False) + + op.create_table('account_fees_history', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('account_id', sa.Integer(), autoincrement=False, nullable=True), + sa.Column('apply_filing_fees', sa.Boolean(), autoincrement=False, nullable=True), + sa.Column('service_fee_code', sa.String(length=10), autoincrement=False, nullable=True), + sa.Column('product', sa.String(length=20), autoincrement=False, nullable=True), + sa.Column('created_on', sa.DateTime(), autoincrement=False, nullable=False), + sa.Column('updated_on', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('created_by', sa.String(length=50), autoincrement=False, nullable=False), + sa.Column('created_name', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('updated_by', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('updated_name', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['account_id'], ['payment_accounts.id'], ), + sa.ForeignKeyConstraint(['service_fee_code'], ['fee_codes.code'], ), + sa.PrimaryKeyConstraint('id', 'version'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('account_fees_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_account_fees_history_account_id'), ['account_id'], unique=False) + + op.create_table('cfs_accounts_history', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('cfs_account', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('cfs_party', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('cfs_site', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('payment_instrument_number', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('contact_party', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('bank_number', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('bank_branch_number', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('status', sa.String(length=40), autoincrement=False, nullable=True), + sa.Column('account_id', sa.Integer(), autoincrement=False, nullable=True), + sa.Column('bank_account_number', sqlalchemy_utils.types.encrypted.encrypted_type.StringEncryptedType(), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['account_id'], ['payment_accounts.id'], ), + sa.ForeignKeyConstraint(['status'], ['cfs_account_status_codes.code'], ), + sa.PrimaryKeyConstraint('id', 'version'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('cfs_accounts_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_cfs_accounts_history_account_id'), ['account_id'], unique=False) + batch_op.create_index(batch_op.f('ix_cfs_accounts_history_bank_account_number'), ['bank_account_number'], unique=False) + batch_op.create_index(batch_op.f('ix_cfs_accounts_history_bank_branch_number'), ['bank_branch_number'], unique=False) + batch_op.create_index(batch_op.f('ix_cfs_accounts_history_bank_number'), ['bank_number'], unique=False) + batch_op.create_index(batch_op.f('ix_cfs_accounts_history_cfs_account'), ['cfs_account'], unique=False) + + op.create_table('distribution_codes_history', + sa.Column('distribution_code_id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('name', sa.String(length=250), autoincrement=False, nullable=True), + sa.Column('client', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('responsibility_centre', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('service_line', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('stob', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('project_code', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('start_date', sa.Date(), autoincrement=False, nullable=False), + sa.Column('end_date', sa.Date(), autoincrement=False, nullable=True), + sa.Column('stop_ejv', sa.Boolean(), autoincrement=False, nullable=True), + sa.Column('service_fee_distribution_code_id', sa.Integer(), autoincrement=False, nullable=True), + sa.Column('disbursement_distribution_code_id', sa.Integer(), autoincrement=False, nullable=True), + sa.Column('account_id', sa.Integer(), autoincrement=False, nullable=True), + sa.Column('created_on', sa.DateTime(), autoincrement=False, nullable=False), + sa.Column('updated_on', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('created_by', sa.String(length=50), autoincrement=False, nullable=False), + sa.Column('created_name', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('updated_by', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('updated_name', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['account_id'], ['payment_accounts.id'], ), + # sa.ForeignKeyConstraint(['disbursement_distribution_code_id'], ['distribution_codes_history.distribution_code_id'], ), + # sa.ForeignKeyConstraint(['service_fee_distribution_code_id'], ['distribution_codes_history.distribution_code_id'], ), + sa.PrimaryKeyConstraint('distribution_code_id', 'version'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('distribution_codes_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_distribution_codes_history_account_id'), ['account_id'], unique=False) + + op.create_table('refunds_partial_history', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('payment_line_item_id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('refund_amount', sa.Numeric(precision=19, scale=2), autoincrement=False, nullable=False), + sa.Column('refund_type', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('disbursement_status_code', sa.String(length=20), autoincrement=False, nullable=True), + sa.Column('disbursement_date', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('created_on', sa.DateTime(), autoincrement=False, nullable=False), + sa.Column('updated_on', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('created_by', sa.String(length=50), autoincrement=False, nullable=False), + sa.Column('created_name', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('updated_by', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('updated_name', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['disbursement_status_code'], ['disbursement_status_codes.code'], ), + sa.ForeignKeyConstraint(['payment_line_item_id'], ['payment_line_items.id'], ), + sa.PrimaryKeyConstraint('id', 'version'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('refunds_partial_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_refunds_partial_history_payment_line_item_id'), ['payment_line_item_id'], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('refunds_partial_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_refunds_partial_history_payment_line_item_id')) + + op.drop_table('refunds_partial_history') + with op.batch_alter_table('distribution_codes_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_distribution_codes_history_account_id')) + + op.drop_table('distribution_codes_history') + with op.batch_alter_table('cfs_accounts_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_cfs_accounts_history_cfs_account')) + batch_op.drop_index(batch_op.f('ix_cfs_accounts_history_bank_number')) + batch_op.drop_index(batch_op.f('ix_cfs_accounts_history_bank_branch_number')) + batch_op.drop_index(batch_op.f('ix_cfs_accounts_history_bank_account_number')) + batch_op.drop_index(batch_op.f('ix_cfs_accounts_history_account_id')) + + op.drop_table('cfs_accounts_history') + with op.batch_alter_table('account_fees_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_account_fees_history_account_id')) + + op.drop_table('account_fees_history') + with op.batch_alter_table('payment_accounts_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_payment_accounts_history_bcol_user_id')) + batch_op.drop_index(batch_op.f('ix_payment_accounts_history_bcol_account')) + batch_op.drop_index(batch_op.f('ix_payment_accounts_history_auth_account_id')) + + op.drop_table('payment_accounts_history') + with op.batch_alter_table('eft_short_names_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_eft_short_names_history_short_name')) + batch_op.drop_index(batch_op.f('ix_eft_short_names_history_auth_account_id')) + + op.drop_table('eft_short_names_history') + + with op.batch_alter_table('refunds_partial', schema=None) as batch_op: + batch_op.drop_column('version') + + with op.batch_alter_table('payment_accounts', schema=None) as batch_op: + batch_op.drop_column('version') + + with op.batch_alter_table('eft_short_names', schema=None) as batch_op: + batch_op.drop_column('version') + + with op.batch_alter_table('distribution_codes', schema=None) as batch_op: + batch_op.drop_column('version') + + with op.batch_alter_table('cfs_accounts', schema=None) as batch_op: + batch_op.drop_column('version') + + with op.batch_alter_table('account_fees', schema=None) as batch_op: + batch_op.drop_column('version') + # ### end Alembic commands ### diff --git a/pay-api/migrations/versions/2024_03_18_52ed2340a43c_move_from_version_to_history.py b/pay-api/migrations/versions/2024_03_18_52ed2340a43c_move_from_version_to_history.py new file mode 100644 index 000000000..7b779fb2a --- /dev/null +++ b/pay-api/migrations/versions/2024_03_18_52ed2340a43c_move_from_version_to_history.py @@ -0,0 +1,359 @@ +"""Move data from version to history table. +A special thanks to LEAR devs (Thor, Argus, Vysakh) for this migration and the history table implementation: +https://github.com/bcgov/lear/blob/feature-legal-name/legal-api/scripts/manual_db_scripts/legal_name_change/transfer_to_new_lear.sql + +Revision ID: 52ed2340a43c +Revises: fb3ba97b603a +Create Date: 2024-03-18 09:53:33.369110 + +""" +from alembic import op + + +# revision identifiers, used by Alembic. +revision = '52ed2340a43c' +down_revision = 'fb3ba97b603a' +branch_labels = None +depends_on = None + + +def upgrade(): + # Currently these are only set to version = 1 + op.execute(""" + update account_fees set version = + (select coalesce( + (select count(transaction_id) as version + from account_fees_version + where + account_fees.id = account_fees_version.id + group by + id + ), 1)); + """) + + op.execute(""" + update cfs_accounts set version = + (select coalesce( + (select count(transaction_id) as version + from cfs_accounts_version + where cfs_accounts.id = cfs_accounts_version.id + group by id + ), 1)); + """) + + op.execute(""" + update distribution_codes set version = + (select coalesce( + (select count(transaction_id) as version + from distribution_codes_version + where distribution_codes.distribution_code_id = distribution_codes_version.distribution_code_id + group by distribution_code_id + ),1)); + """) + + op.execute(""" + update eft_short_names set version = + (select coalesce( + (select count(transaction_id) as version + from eft_short_names_version + where eft_short_names.id = eft_short_names_version.id + group by id + ),1)); + """) + + op.execute(""" + update payment_accounts set version = + (select coalesce( + (select count(transaction_id) as version + from payment_accounts_version + where payment_accounts.id = payment_accounts_version.id + group by id + ),1)); + """) + + op.execute(""" + update refunds_partial set version = + (select coalesce( + (select count(transaction_id) as version + from refunds_partial_version + where refunds_partial.id = refunds_partial_version.id + group by id + ),1)); + """) + + op.execute(""" + with subquery as ( + select + afv.id, + account_id, + apply_filing_fees, + service_fee_code, + product, + created_on, + updated_on, + created_by, + created_name, + updated_by, + updated_name, + t.issued_at as changed, + COALESCE(ROW_NUMBER() OVER (PARTITION BY afv.id ORDER BY afv.transaction_id ASC), 1) as version + from + account_fees_version afv + left join + public.transaction t on afv.transaction_id = t.id + ), + max_versions as ( + select + id, + max(version) as max_version + from + subquery sq + group by id + ) + + insert into + account_fees_history (id, apply_filing_fees, service_fee_code, product, created_on, updated_on, + created_by, created_name, updated_by, updated_name, changed, version) + select + sq.id, apply_filing_fees, service_fee_code, product, created_on, updated_on, + created_by, created_name, updated_by, updated_name, changed, version + from + subquery sq + left join + max_versions mv on mv.id = sq.id + where + sq.version != mv.max_version; + """) + + op.execute(""" + with subquery as ( + select + cav.id, + cfs_account, + cfs_party, + cfs_site, + payment_instrument_number, + contact_party, + bank_number, + bank_branch_number, + t.issued_at as changed, + COALESCE(ROW_NUMBER() OVER (PARTITION BY cav.id ORDER BY cav.transaction_id ASC), 1) as version + from cfs_accounts_version cav + left join public.transaction t on cav.transaction_id = t.id + ), + max_versions as ( + select + id, + max(version) as max_version + from + subquery sq + group by id + ) + insert into + cfs_accounts_history (id, cfs_account, cfs_party, cfs_site, payment_instrument_number, + contact_party, bank_number, bank_branch_number, changed, version) + select + sq.id, cfs_account, cfs_party, cfs_site, payment_instrument_number, + contact_party, bank_number, bank_branch_number, changed, version + from + subquery sq + left join + max_versions mv on mv.id = sq.id + where + sq.version != mv.max_version; + """) + + op.execute(""" + with subquery as ( + select + dcv.distribution_code_id, + name, + client, + responsibility_centre, + service_line, + stob, + project_code, + start_date, + end_date, + stop_ejv, + service_fee_distribution_code_id, + disbursement_distribution_code_id, + account_id, + created_on, + updated_on, + created_by, + created_name, + updated_by, + updated_name, + t.issued_at as changed, + COALESCE(ROW_NUMBER() OVER (PARTITION BY dcv.distribution_code_id ORDER BY dcv.transaction_id ASC), 1) as version + from distribution_codes_version dcv + left join public.transaction t on dcv.transaction_id = t.id + ), + max_versions as ( + select + sq.distribution_code_id, + max(version) as max_version + from subquery sq + group by sq.distribution_code_id + ) + + insert into + distribution_codes_history (distribution_code_id, name, client, responsibility_centre, + service_line, stob, project_code, start_date, end_date, stop_ejv, service_fee_distribution_code_id, + disbursement_distribution_code_id, account_id, created_on, updated_on, created_by, created_name, + updated_by, updated_name, changed, version) + select + sq.distribution_code_id, name, client, responsibility_centre, + service_line, stob, project_code, start_date, end_date, stop_ejv, service_fee_distribution_code_id, + disbursement_distribution_code_id, account_id, created_on, updated_on, created_by, created_name, + updated_by, updated_name, changed, version + from + subquery sq + left join + max_versions mv on mv.distribution_code_id = sq.distribution_code_id + where sq.version != mv.max_version; + """) + + op.execute(""" + with subquery as ( + select + esnv.id, + auth_account_id, + created_on, + short_name, + linked_by, + linked_by_name, + linked_on, + t.issued_at as changed, + COALESCE(ROW_NUMBER() OVER (PARTITION BY esnv.id ORDER BY esnv.transaction_id ASC), 1) as version + from eft_short_names_version esnv + left join public.transaction t on esnv.transaction_id = t.id + ), + max_versions as ( + select + id, + max(version) as max_version + from + subquery sq + group by id + ) + insert into + eft_short_names_history (id, auth_account_id, created_on, short_name, linked_by, linked_by_name, linked_on, changed, version) + select + sq.id, auth_account_id, created_on, short_name, linked_by, linked_by_name, linked_on, changed, version + from + subquery sq + left join + max_versions mv on mv.id = sq.id + where sq.version != mv.max_version; + """) + + op.execute(""" + with subquery as ( + select + pav.id, + auth_account_id, + name, + branch_name, + payment_method, + bcol_user_id, + bcol_account, + statement_notification_enabled, + credit, + billable, + eft_enable, + pad_activation_date, + pad_tos_accepted_date, + pad_tos_accepted_by, + t.issued_at as changed, + COALESCE(ROW_NUMBER() OVER (PARTITION BY pav.id ORDER BY pav.transaction_id ASC), 1) as version + from + payment_accounts_version pav + left join + public.transaction t on pav.transaction_id = t.id + ), + max_versions as ( + select + id, + max(version) as max_version + from + subquery sq + group by id + ) + insert into + payment_accounts_history (id, auth_account_id, name, branch_name, payment_method, bcol_user_id, + bcol_account, statement_notification_enabled, credit, billable, eft_enable, pad_activation_date, + pad_tos_accepted_date, pad_tos_accepted_by, changed, version) + select + sq.id, auth_account_id, name, branch_name, payment_method, bcol_user_id, + bcol_account, statement_notification_enabled, credit, billable, eft_enable, pad_activation_date, + pad_tos_accepted_date, pad_tos_accepted_by, changed, version + from + subquery sq + left join + max_versions mv on mv.id = sq.id + where + sq.version != mv.max_version; + """) + + op.execute(""" + with subquery as ( + select + rpv.id, + payment_line_item_id, + refund_amount, + refund_type, + disbursement_status_code, + disbursement_date, + created_on, + updated_on, + created_by, + created_name, + updated_by, + updated_name, + t.issued_at as changed, + COALESCE(ROW_NUMBER() OVER (PARTITION BY rpv.id ORDER BY rpv.transaction_id ASC), 1) as version + from + refunds_partial_version rpv + left join + public.transaction t on rpv.transaction_id = t.id + ), + max_versions as ( + select + id, + max(version) as max_version + from subquery sq + group by id + ) + + insert into + refunds_partial_history (id, payment_line_item_id, refund_amount, refund_type, disbursement_status_code, + disbursement_date, created_on, updated_on, created_by, created_name, updated_by, + updated_name, changed, version) + select + sq.id, payment_line_item_id, refund_amount, refund_type, disbursement_status_code, + disbursement_date, created_on, updated_on, created_by, created_name, updated_by, + updated_name, changed, version + from + subquery sq + left join + max_versions mv on mv.id = sq.id + where + sq.version != mv.max_version; + """) + + +def downgrade(): + op.execute('update refunds_partial set version = 1;') + op.execute('update payment_accounts set version = 1;') + op.execute('update eft_short_names set version = 1;') + op.execute('update distribution_codes set version = 1;') + op.execute('update cfs_accounts set version = 1;') + op.execute('update account_fees set version = 1;') + op.execute('delete from refunds_partial_history;') + op.execute('delete from payment_accounts_history;') + op.execute('delete from eft_short_names_history;') + op.execute('delete from distribution_codes_history;') + op.execute('delete from cfs_accounts_history;') + op.execute('delete from account_fees_history;') diff --git a/pay-api/migrations/versions/2024_04_12_29867cf1bd9e_19496_eft_short_names_updates.py b/pay-api/migrations/versions/2024_04_12_29867cf1bd9e_19496_eft_short_names_updates.py new file mode 100644 index 000000000..bba99e3d7 --- /dev/null +++ b/pay-api/migrations/versions/2024_04_12_29867cf1bd9e_19496_eft_short_names_updates.py @@ -0,0 +1,123 @@ +"""19496-eft-shortname-updates + +Revision ID: 29867cf1bd9e +Revises: 52ed2340a43c +Create Date: 2024-04-12 10:21:53.863572 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy import text +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '29867cf1bd9e' +down_revision = '52ed2340a43c' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + + # Table to support multiple accounts linked to a short name with different statuses + op.create_table('eft_short_name_links', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('eft_short_name_id', sa.Integer(), nullable=False), + sa.Column('auth_account_id', sa.String(length=50), nullable=False), + sa.Column('created_on', sa.DateTime(), nullable=False), + sa.Column('status_code', sa.String(length=25), nullable=False), + sa.Column('updated_by', sa.String(length=100), nullable=True), + sa.Column('updated_by_name', sa.String(length=100), nullable=True), + sa.Column('updated_on', sa.DateTime(), nullable=True), + sa.Column('version', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['eft_short_name_id'], ['eft_short_names.id'], ), + sa.PrimaryKeyConstraint('id'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('eft_short_name_links', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_eft_short_name_links_auth_account_id'), ['auth_account_id'], unique=False) + batch_op.create_index(batch_op.f('ix_eft_short_name_links_eft_short_name_id'), ['eft_short_name_id'], unique=False) + batch_op.create_index(batch_op.f('ix_eft_short_name_links_status_code'), ['status_code'], unique=False) + + op.create_table('eft_short_name_links_history', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('eft_short_name_id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('auth_account_id', sa.String(length=50), autoincrement=False, nullable=False), + sa.Column('created_on', sa.DateTime(), autoincrement=False, nullable=False), + sa.Column('status_code', sa.String(length=25), autoincrement=False, nullable=False), + sa.Column('updated_by', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('updated_by_name', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('updated_on', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['eft_short_name_id'], ['eft_short_names.id'], ), + sa.PrimaryKeyConstraint('id', 'version'), + sqlite_autoincrement=True + ) + + with op.batch_alter_table('eft_short_name_links_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_eft_short_name_links_history_auth_account_id'), ['auth_account_id'], unique=False) + batch_op.create_index(batch_op.f('ix_eft_short_name_links_history_eft_short_name_id'), ['eft_short_name_id'], unique=False) + batch_op.create_index(batch_op.f('ix_eft_short_name_links_history_status_code'), ['status_code'], unique=False) + + # Migrate linking information from existing short name table + op.execute(text(f"INSERT INTO eft_short_name_links (eft_short_name_id, auth_account_id, created_on, status_code, " + f"updated_by, updated_by_name, updated_on, version) " + f"select id, auth_account_id, created_on, 'LINKED', linked_by, linked_by_name, linked_on, 1 " + f"from eft_short_names where auth_account_id is not null")) + + with op.batch_alter_table('eft_short_names', schema=None) as batch_op: + batch_op.drop_index('ix_eft_short_names_auth_account_id') + batch_op.drop_column('auth_account_id') + batch_op.drop_column('linked_by_name') + batch_op.drop_column('linked_by') + batch_op.drop_column('linked_on') + + with op.batch_alter_table('eft_short_names_history', schema=None) as batch_op: + batch_op.drop_index('ix_eft_short_names_history_auth_account_id') + batch_op.drop_column('auth_account_id') + batch_op.drop_column('linked_by_name') + batch_op.drop_column('linked_by') + batch_op.drop_column('linked_on') + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('eft_short_names_history', schema=None) as batch_op: + batch_op.add_column(sa.Column('linked_on', postgresql.TIMESTAMP(), autoincrement=False, nullable=True)) + batch_op.add_column(sa.Column('linked_by', sa.VARCHAR(length=100), autoincrement=False, nullable=True)) + batch_op.add_column(sa.Column('linked_by_name', sa.VARCHAR(length=100), autoincrement=False, nullable=True)) + batch_op.add_column(sa.Column('auth_account_id', sa.VARCHAR(length=50), autoincrement=False, nullable=True)) + batch_op.create_index('ix_eft_short_names_history_auth_account_id', ['auth_account_id'], unique=False) + + with op.batch_alter_table('eft_short_names', schema=None) as batch_op: + batch_op.add_column(sa.Column('linked_on', postgresql.TIMESTAMP(), autoincrement=False, nullable=True)) + batch_op.add_column(sa.Column('linked_by', sa.VARCHAR(length=100), autoincrement=False, nullable=True)) + batch_op.add_column(sa.Column('linked_by_name', sa.VARCHAR(length=100), autoincrement=False, nullable=True)) + batch_op.add_column(sa.Column('auth_account_id', sa.VARCHAR(length=50), autoincrement=False, nullable=True)) + batch_op.create_index('ix_eft_short_names_auth_account_id', ['auth_account_id'], unique=False) + + # Restore data from eft short name link table + op.execute(text(f"UPDATE eft_short_names esn " + f"SET auth_account_id = (select auth_account_id from eft_short_name_links esnl where esnl.eft_short_name_id = esn.id)," + f"linked_on = (select esnl.updated_on from eft_short_name_links esnl where esnl.eft_short_name_id = esn.id)," + f"linked_by = (select esnl.updated_by from eft_short_name_links esnl where esnl.eft_short_name_id = esn.id)," + f"linked_by_name = (select esnl.updated_by_name from eft_short_name_links esnl where esnl.eft_short_name_id = esn.id)")) + + with op.batch_alter_table('eft_short_name_links_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_eft_short_name_links_history_status_code')) + batch_op.drop_index(batch_op.f('ix_eft_short_name_links_history_eft_short_name_id')) + batch_op.drop_index(batch_op.f('ix_eft_short_name_links_history_auth_account_id')) + + op.drop_table('eft_short_name_links_history') + with op.batch_alter_table('eft_short_name_links', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_eft_short_name_links_status_code')) + batch_op.drop_index(batch_op.f('ix_eft_short_name_links_eft_short_name_id')) + batch_op.drop_index(batch_op.f('ix_eft_short_name_links_auth_account_id')) + + op.drop_table('eft_short_name_links') + + # ### end Alembic commands ### diff --git a/pay-api/migrations/versions/2024_05_07_4b3bd37727a5_20417_eft_credit_invoice_links.py b/pay-api/migrations/versions/2024_05_07_4b3bd37727a5_20417_eft_credit_invoice_links.py new file mode 100644 index 000000000..ee102ecb6 --- /dev/null +++ b/pay-api/migrations/versions/2024_05_07_4b3bd37727a5_20417_eft_credit_invoice_links.py @@ -0,0 +1,35 @@ +"""Add better tracking to how much EFT credit is spent as it is possible an invoice is paid through multiple +EFT Transactions. A status is also added to allow for EFT credits to be applied later via a job. + +Revision ID: 4b3bd37727a5 +Revises: 29867cf1bd9e +Create Date: 2024-05-07 08:32:12.812898 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy import text +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '4b3bd37727a5' +down_revision = '29867cf1bd9e' +branch_labels = None +depends_on = None + + +def upgrade(): + with op.batch_alter_table('eft_credit_invoice_links', schema=None) as batch_op: + batch_op.add_column(sa.Column('amount', sa.Numeric(precision=19, scale=2), nullable=True)) + batch_op.add_column(sa.Column('status_code', sa.String(length=25), nullable=True)) + batch_op.create_index(batch_op.f('ix_eft_credit_invoice_links_status_code'), ['status_code'], unique=False) + + op.execute(text(f"UPDATE eft_credit_invoice_links " + f"SET status_code = 'PENDING'")) + + +def downgrade(): + with op.batch_alter_table('eft_credit_invoice_links', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_eft_credit_invoice_links_status_code')) + batch_op.drop_column('status_code') + batch_op.drop_column('amount') diff --git a/pay-api/migrations/versions/331c2cdb0b94_fix_payment_account_id_in_payment_table.py b/pay-api/migrations/versions/331c2cdb0b94_fix_payment_account_id_in_payment_table.py index 394956987..97b12f07a 100644 --- a/pay-api/migrations/versions/331c2cdb0b94_fix_payment_account_id_in_payment_table.py +++ b/pay-api/migrations/versions/331c2cdb0b94_fix_payment_account_id_in_payment_table.py @@ -30,12 +30,12 @@ def upgrade(): # 2. Find the account id from invoice table linking with invoice_reference table. # 3. Update payment with payment account id. conn = op.get_bind() - res = conn.execute(f"select id,invoice_number from payment where payment_account_id is null;") + res = conn.execute(sa.text(f"select id,invoice_number from payment where payment_account_id is null;")) results = res.fetchall() for result in results: pay_id = result[0] invoice_number = result[1] - res = conn.execute(f"select payment_account_id from invoice where id=(select invoice_id from invoice_reference where invoice_number='{invoice_number}');") + res = conn.execute(sa.text(f"select payment_account_id from invoice where id=(select invoice_id from invoice_reference where invoice_number='{invoice_number}');")) payment_account_id_result = res.fetchall() if payment_account_id_result: payment_account_id = payment_account_id_result[0][0] diff --git a/pay-api/migrations/versions/4cb0dc8e0013_ppr_and_dissolution_filing_type_codes.py b/pay-api/migrations/versions/4cb0dc8e0013_ppr_and_dissolution_filing_type_codes.py index a3e5c2352..4172081c9 100644 --- a/pay-api/migrations/versions/4cb0dc8e0013_ppr_and_dissolution_filing_type_codes.py +++ b/pay-api/migrations/versions/4cb0dc8e0013_ppr_and_dissolution_filing_type_codes.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, String +from sqlalchemy import Date, String, text from sqlalchemy.sql import column, table # revision identifiers, used by Alembic. @@ -410,11 +410,11 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - f"select fee_schedule_id from fee_schedules where corp_type_code='PPR' and filing_type_code='SSRCH'") + text(f"select fee_schedule_id from fee_schedules where corp_type_code='PPR' and filing_type_code='SSRCH'")) fee_schedule_id = res.fetchall()[0][0] distr_code_link = [{ 'distribution_code_id': distribution_code_id, @@ -428,11 +428,11 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - f"select fee_schedule_id from fee_schedules where filing_type_code in ('DIS_VOL', 'DIS_INVOL', 'DIS_ADMIN', 'DIS_LQD', 'DIS_COLQD', 'DIS_RSTR', 'AFDVT', 'SPRLN')") + text(f"select fee_schedule_id from fee_schedules where filing_type_code in ('DIS_VOL', 'DIS_INVOL', 'DIS_ADMIN', 'DIS_LQD', 'DIS_COLQD', 'DIS_RSTR', 'AFDVT', 'SPRLN')")) distr_code_links = [] for result in res.fetchall(): fee_schedule_id = result[0] diff --git a/pay-api/migrations/versions/4cc39da0bee7_payment_system_account.py b/pay-api/migrations/versions/4cc39da0bee7_payment_system_account.py index 50ff74e42..489d64aa2 100644 --- a/pay-api/migrations/versions/4cc39da0bee7_payment_system_account.py +++ b/pay-api/migrations/versions/4cc39da0bee7_payment_system_account.py @@ -78,25 +78,6 @@ def upgrade(): op.add_column('invoice', sa.Column('corp_type_code', sa.String(length=10), nullable=True)) op.create_foreign_key(None, 'invoice', 'corp_type', ['corp_type_code'], ['code']) - # Create temp variable to keep the payment accounts - # payment_accounts: [] = [] - # conn = op.get_bind() - # res = conn.execute( - # f"select id,corp_number,corp_type_code,payment_system_code,account_number,party_number,site_number from payment_account;") - # results = res.fetchall() - # for result in results: - # payment_accounts.append( - # { - # "id": result[0], - # "corp_number": result[1], - # "corp_type_code": result[2], - # "payment_system_code": result[3], - # "account_number": result[4], - # "party_number": result[5], - # "site_number": result[6] - # } - # ) - # Now drop the columns from payment_account table op.drop_index('ix_payment_account_account_number', table_name='payment_account') op.drop_index('ix_payment_account_bcol_account_id', table_name='payment_account') @@ -114,118 +95,6 @@ def upgrade(): # Delete all records from payment_account op.execute('delete from payment_account;') - - # Create admin token to call auth-api - # config = current_app.config - # token_url = config.get( - # 'JWT_OIDC_ISSUER') + '/protocol/openid-connect/token' # https://sso-dev.pathfinder.gov.bc.ca/auth/realms/fcf0kpqr/protocol/openid-connect/token - # basic_auth_encoded = base64.b64encode( - # bytes(config.get('KEYCLOAK_SERVICE_ACCOUNT_ID') + ':' + config.get('KEYCLOAK_SERVICE_ACCOUNT_SECRET'), - # 'utf-8')).decode('utf-8') - # data = 'grant_type=client_credentials' - # token_response = OAuthService.post(token_url, basic_auth_encoded, AuthHeaderType.BASIC, - # ContentType.FORM_URL_ENCODED, data) - # token = token_response.json().get('access_token') - # - # payment_account_table = table('payment_account', column('auth_account_id', String)) - # credit_payment_account_table = table('credit_payment_account', - # column('corp_number', String), - # column('corp_type_code', String), - # column('paybc_account', String), - # column('paybc_party', String), - # column('paybc_site', String), - # column('account_id', Integer)) - # - # internal_payment_account_table = table('internal_payment_account', - # column('corp_number', String), - # column('corp_type_code', String), - # column('account_id', Integer)) - # - # for payment_account in payment_accounts: - # corp_number = payment_account.get('corp_number') - # payment_system_code = payment_account.get('payment_system_code') - # corp_type_code = payment_account.get('corp_type_code') - # account_number = payment_account.get('account_number') - # party_number = payment_account.get('party_number') - # site_number = payment_account.get('site_number') - # id = payment_account.get('id') - # - # if corp_number: - # # Call Auth-API to get the auth_account id - # auth_search_url = config.get( - # 'AUTH_API_ENDPOINT') + 'orgs?affiliation=' + corp_number.upper() - # orgs_response = None - # try: - # orgs_response = OAuthService.get(auth_search_url, token, AuthHeaderType.BEARER, - # ContentType.JSON).json().get('orgs') - # except Exception as e: - # print(f'--- Error Occured while getting org for affiliation {corp_number}') - # - # if orgs_response and orgs_response[0]: - # auth_account_id = str(orgs_response[0].get('id')) - # else: - # auth_account_id = f'PASSCODE_ACCOUNT_{corp_number}' - # - # res = conn.execute(f"select id from payment_account where auth_account_id = '{auth_account_id}'") - # results = res.fetchall() - # - # credit_payment_account_id = None - # internal_payment_account_id = None - # if results and results[0]: - # payment_account_id = results[0][0] - # else: - # op.bulk_insert( - # payment_account_table, [ - # {"auth_account_id": auth_account_id} - # ] - # ) - # res = conn.execute( - # f"select id from payment_account where auth_account_id = '{auth_account_id}'") - # results = res.fetchall() - # payment_account_id = results[0][0] - # - # if payment_system_code == 'PAYBC': - # print(f'Creating credit account for {payment_account_id}') - # op.bulk_insert( - # credit_payment_account_table, [ - # { - # "corp_number": corp_number, - # "corp_type_code": corp_type_code, - # "paybc_account": account_number, - # "paybc_party": party_number, - # "paybc_site": site_number, - # "account_id": payment_account_id - # } - # ] - # ) - # res = conn.execute( - # f"select id from credit_payment_account where account_id = '{payment_account_id}' and corp_number='{corp_number}' and paybc_account='{account_number}'") - # results = res.fetchall() - # credit_payment_account_id = results[0][0] - # elif payment_system_code == 'INTERNAL': - # print(f'Creating internal payment account for {payment_account_id}') - # op.bulk_insert( - # internal_payment_account_table, [ - # { - # "corp_number": corp_number, - # "corp_type_code": corp_type_code, - # "account_id": payment_account_id - # } - # ] - # ) - # res = conn.execute( - # f"select id from internal_payment_account where account_id = '{payment_account_id}' and corp_number='{corp_number}' and corp_type_code='{corp_type_code}'") - # results = res.fetchall() - # internal_payment_account_id = results[0][0] - # print('Internal ', internal_payment_account_id) - # print('Credit ', credit_payment_account_id) - # if internal_payment_account_id: - # op.execute( - # f"update invoice set internal_account_id = '{internal_payment_account_id}', corp_type_code='{corp_type_code}', business_identifier='{corp_number}' where account_id = '{id}'") - # elif credit_payment_account_id: - # op.execute( - # f"update invoice set credit_account_id = '{credit_payment_account_id}', corp_type_code='{corp_type_code}', business_identifier='{corp_number}' where account_id = '{id}'") - op.drop_column('invoice', 'account_id') # ### end Alembic commands ### diff --git a/pay-api/migrations/versions/56c5db141909_refactor_payment_account.py b/pay-api/migrations/versions/56c5db141909_refactor_payment_account.py index 25bdc7a84..19dca034b 100644 --- a/pay-api/migrations/versions/56c5db141909_refactor_payment_account.py +++ b/pay-api/migrations/versions/56c5db141909_refactor_payment_account.py @@ -65,7 +65,7 @@ def upgrade(): # Update payment account id here conn = op.get_bind() - res = conn.execute("select id, bcol_user_id, bcol_account_id, account_id from bcol_payment_account;") + res = conn.execute(sa.text("select id, bcol_user_id, bcol_account_id, account_id from bcol_payment_account;")) bcol_payment_accounts = res.fetchall() for bcol_payment_account in bcol_payment_accounts: id = bcol_payment_account[0] @@ -82,7 +82,7 @@ def upgrade(): # Insert cfs_account details and mark it as inactive, as all the existing accounts are entity based. op.execute("insert into cfs_account (account_id, cfs_account, cfs_party, cfs_site, is_active) select account_id, paybc_account, paybc_party, paybc_site, false from credit_payment_account") - res = conn.execute("select id, account_id from credit_payment_account;") + res = conn.execute(sa.text("select id, account_id from credit_payment_account;")) credit_payment_accounts = res.fetchall() for credit_payment_account in credit_payment_accounts: id = credit_payment_account[0] @@ -91,7 +91,7 @@ def upgrade(): # Update payment account with bcol information op.execute(f"update invoice set payment_account_id={account_id} where credit_account_id='{id}'") - res = conn.execute("select id, account_id from internal_payment_account;") + res = conn.execute(sa.text("select id, account_id from internal_payment_account;")) internal_payment_accounts = res.fetchall() for internal_payment_account in internal_payment_accounts: id = internal_payment_account[0] diff --git a/pay-api/migrations/versions/5d9997f7e649_variable_fee_code.py b/pay-api/migrations/versions/5d9997f7e649_variable_fee_code.py index 7fc48c4f6..ffbe5a240 100644 --- a/pay-api/migrations/versions/5d9997f7e649_variable_fee_code.py +++ b/pay-api/migrations/versions/5d9997f7e649_variable_fee_code.py @@ -117,12 +117,12 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(sa.text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - "select fee_schedule_id from fee_schedules where corp_type_code='CSO' and " - "filing_type_code in ('CSBVFEE', 'CSBSRCH', 'CSBPDOC', 'CSCRMTFC')") + sa.text("select fee_schedule_id from fee_schedules where corp_type_code='CSO' and " + "filing_type_code in ('CSBVFEE', 'CSBSRCH', 'CSBPDOC', 'CSCRMTFC')")) distr_code_links = [] for result in res.fetchall(): diff --git a/pay-api/migrations/versions/609b98d87a72_rpt.py b/pay-api/migrations/versions/609b98d87a72_rpt.py index c8521d8bc..0f720fdaa 100644 --- a/pay-api/migrations/versions/609b98d87a72_rpt.py +++ b/pay-api/migrations/versions/609b98d87a72_rpt.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, String, Boolean +from sqlalchemy import Date, String, Boolean, text from sqlalchemy.sql import column, table # revision identifiers, used by Alembic. @@ -77,11 +77,11 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - f"select fee_schedule_id from fee_schedules where corp_type_code='RPT' and filing_type_code='OLLXTFI'") + text(f"select fee_schedule_id from fee_schedules where corp_type_code='RPT' and filing_type_code='OLLXTFI'")) fee_schedule_id = res.fetchall()[0][0] distr_code_link = [{ 'distribution_code_id': distribution_code_id, diff --git a/pay-api/migrations/versions/643790dd3334_cso.py b/pay-api/migrations/versions/643790dd3334_cso.py index c7bfb2eab..33957916e 100644 --- a/pay-api/migrations/versions/643790dd3334_cso.py +++ b/pay-api/migrations/versions/643790dd3334_cso.py @@ -80,11 +80,11 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(sa.text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - f"select fee_schedule_id from fee_schedules where corp_type_code='CSO' and filing_type_code='CSBFILE'") + sa.text(f"select fee_schedule_id from fee_schedules where corp_type_code='CSO' and filing_type_code='CSBFILE'")) fee_schedule_id = res.fetchall()[0][0] distr_code_link = [{ 'distribution_code_id': distribution_code_id, diff --git a/pay-api/migrations/versions/6a6b042b831a_.py b/pay-api/migrations/versions/6a6b042b831a_.py index 91adb62ec..adf1185f5 100644 --- a/pay-api/migrations/versions/6a6b042b831a_.py +++ b/pay-api/migrations/versions/6a6b042b831a_.py @@ -60,8 +60,8 @@ def upgrade(): ) distribution_code_link_table = table('distribution_code_links', - column('distribution_code_id', String), - column('fee_schedule_id', String) + column('distribution_code_id', Integer), + column('fee_schedule_id', Integer) ) # Product code/corp type @@ -113,13 +113,13 @@ def upgrade(): distribution_code_id_query = "select distribution_code_id from distribution_codes where name = 'Corporate Registry - Searches' and created_by = 'Alembic'" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(sa.text(distribution_code_id_query)) distribution_code_id = res.fetchall()[0][0] new_codes = ('BSRCH',) distr_code_link_values = [] for new_code in new_codes: res = conn.execute( - f"select fee_schedule_id from fee_schedules where filing_type_code='{new_code}' and corp_type_code='BUS'") + sa.text(f"select fee_schedule_id from fee_schedules where filing_type_code='{new_code}' and corp_type_code='BUS'")) fee_schedule_id = res.fetchall()[0][0] distr_code_link_values.append({ 'distribution_code_id': distribution_code_id, diff --git a/pay-api/migrations/versions/6f0fe9f23d8c_distribution_code_changes.py b/pay-api/migrations/versions/6f0fe9f23d8c_distribution_code_changes.py index fd47a9b07..4765ea3ed 100644 --- a/pay-api/migrations/versions/6f0fe9f23d8c_distribution_code_changes.py +++ b/pay-api/migrations/versions/6f0fe9f23d8c_distribution_code_changes.py @@ -74,8 +74,8 @@ def upgrade(): conn = op.get_bind() res = conn.execute( - " select distinct service_fee_memo_name, service_fee_stob, service_fee_line, service_fee_client, service_fee_project_code, service_fee_responsibility_centre, distribution_code_id " - " from distribution_codes where service_fee_memo_name is not null and service_fee_stob is not null;") + sa.text(" select distinct service_fee_memo_name, service_fee_stob, service_fee_line, service_fee_client, service_fee_project_code, service_fee_responsibility_centre, distribution_code_id " + " from distribution_codes where service_fee_memo_name is not null and service_fee_stob is not null;")) results = res.fetchall() for result in results: service_fee_memo_name = result[0] @@ -88,10 +88,10 @@ def upgrade(): print('distribution_code_id..', distribution_code_id) # Check if there is a record existing with the service_fee_responsibility_centre as that's unique. - res = conn.execute(f"select distribution_code_id from distribution_codes " + res = conn.execute(sa.text(f"select distribution_code_id from distribution_codes " f"where stob='{service_fee_stob}' and service_line='{service_fee_line}' and client='{service_fee_client}' " f" and project_code='{service_fee_project_code}' and responsibility_centre='{service_fee_responsibility_centre}' " - f"and service_fee_distribution_code_id is null") + f"and service_fee_distribution_code_id is null")) results = res.fetchall() service_fee_distribution_code_id = None if len(results) == 0 else results[0][0] print('service_fee_distribution_code_id --->', service_fee_distribution_code_id) @@ -102,10 +102,10 @@ def upgrade(): f"'{service_fee_stob}', '{service_fee_project_code}', '{today}', 'alembic');") # Get the inserted record id - res = conn.execute(f"select distribution_code_id from distribution_codes " + res = conn.execute(sa.text(f"select distribution_code_id from distribution_codes " f"where stob='{service_fee_stob}' and service_line='{service_fee_line}' and client='{service_fee_client}' " f" and project_code='{service_fee_project_code}' and responsibility_centre='{service_fee_responsibility_centre}' " - f"and service_fee_distribution_code_id is null") + f"and service_fee_distribution_code_id is null")) service_fee_distribution_code_id = res.fetchall()[0][0] diff --git a/pay-api/migrations/versions/7c19ee3a58aa_invoice_amount_to_payment.py b/pay-api/migrations/versions/7c19ee3a58aa_invoice_amount_to_payment.py index 7a28f0090..40e43055e 100644 --- a/pay-api/migrations/versions/7c19ee3a58aa_invoice_amount_to_payment.py +++ b/pay-api/migrations/versions/7c19ee3a58aa_invoice_amount_to_payment.py @@ -24,8 +24,8 @@ def upgrade(): op.add_column('payment', sa.Column('invoice_amount', sa.Numeric(), nullable=True)) # lookup the invoice table 'total' field by linking with invoice_reference conn = op.get_bind() - res = conn.execute("select inv.total, inv_ref.invoice_number, inv.payment_account_id " - "from invoice inv, invoice_reference inv_ref where inv.id=inv_ref.invoice_id") + res = conn.execute(sa.text("select inv.total, inv_ref.invoice_number, inv.payment_account_id " + "from invoice inv, invoice_reference inv_ref where inv.id=inv_ref.invoice_id")) invoices = res.fetchall() diff --git a/pay-api/migrations/versions/8652bf9c03ff_payment_to_invoice.py b/pay-api/migrations/versions/8652bf9c03ff_payment_to_invoice.py index ccaaf227d..9b28ea9d3 100644 --- a/pay-api/migrations/versions/8652bf9c03ff_payment_to_invoice.py +++ b/pay-api/migrations/versions/8652bf9c03ff_payment_to_invoice.py @@ -47,7 +47,7 @@ def upgrade(): # Select payment method from payment table and update to invoice table. conn = op.get_bind() - res = conn.execute("select id, payment_method_code, payment_status_code from payment ") + res = conn.execute(sa.text("select id, payment_method_code, payment_status_code from payment ")) payments = res.fetchall() for payment in payments: @@ -78,7 +78,7 @@ def upgrade(): # Populate invoice number to payment table. conn = op.get_bind() - res = conn.execute("select id, invoice_status_code, payment_id from invoice ") + res = conn.execute(sa.text("select id, invoice_status_code, payment_id from invoice ")) invoices = res.fetchall() for invoice in invoices: inv_id = invoice[0] diff --git a/pay-api/migrations/versions/8eac44955fb6_add_created_user_display_names.py b/pay-api/migrations/versions/8eac44955fb6_add_created_user_display_names.py index 6c2cbdef9..80d78ef36 100644 --- a/pay-api/migrations/versions/8eac44955fb6_add_created_user_display_names.py +++ b/pay-api/migrations/versions/8eac44955fb6_add_created_user_display_names.py @@ -34,9 +34,9 @@ def upgrade(): # Check if there are records in payment or invoice table to update names conn = op.get_bind() - res = conn.execute(f"select id from payment;") + res = conn.execute(sa.text(f"select id from payment;")) pay_results = res.fetchall() - res = conn.execute(f"select id from invoice;") + res = conn.execute(sa.text(f"select id from invoice;")) inv_results = res.fetchall() if len(pay_results) > 0 or len(inv_results) > 0: diff --git a/pay-api/migrations/versions/a11be9fe1a6a_distribution_code.py b/pay-api/migrations/versions/a11be9fe1a6a_distribution_code.py index ba9ab674f..4ce3791d9 100644 --- a/pay-api/migrations/versions/a11be9fe1a6a_distribution_code.py +++ b/pay-api/migrations/versions/a11be9fe1a6a_distribution_code.py @@ -97,7 +97,7 @@ def upgrade(): 'distribution_code_id': 4, 'created_by': 'Alembic', 'created_on': date.today(), - 'memo_name': 'TODO-VS', + 'memo_name': 'VS', 'service_fee_memo_name': 'SBC Modernization Service Charge', 'start_date': date.today(), }, @@ -105,7 +105,7 @@ def upgrade(): 'distribution_code_id': 5, 'created_by': 'Alembic', 'created_on': date.today(), - 'memo_name': 'TODO-PPR', + 'memo_name': 'PPR', 'service_fee_memo_name': 'SBC Modernization Service Charge', 'start_date': date.today(), } @@ -115,7 +115,7 @@ def upgrade(): conn = op.get_bind() res = conn.execute( - 'select fee_schedule_id, corp_type_code from fee_schedule;') + sa.text('select fee_schedule_id, corp_type_code from fee_schedule;')) results = res.fetchall() for result in results: distribution_code_id = None diff --git a/pay-api/migrations/versions/b336780735dc_registration_filing_type.py b/pay-api/migrations/versions/b336780735dc_registration_filing_type.py index 2fbbbad19..43adb8c88 100644 --- a/pay-api/migrations/versions/b336780735dc_registration_filing_type.py +++ b/pay-api/migrations/versions/b336780735dc_registration_filing_type.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, String, Boolean, Float +from sqlalchemy import Date, String, Boolean, Float, text from sqlalchemy.sql import column, table # revision identifiers, used by Alembic. @@ -118,11 +118,11 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - "select fee_schedule_id from fee_schedules where filing_type_code = 'FRREG'") + text("select fee_schedule_id from fee_schedules where filing_type_code = 'FRREG'")) distr_code_links = [] for result in res.fetchall(): diff --git a/pay-api/migrations/versions/b6e28faea978_rename_tables_to_plurals.py b/pay-api/migrations/versions/b6e28faea978_rename_tables_to_plurals.py index a473642fc..a2bceb3ae 100644 --- a/pay-api/migrations/versions/b6e28faea978_rename_tables_to_plurals.py +++ b/pay-api/migrations/versions/b6e28faea978_rename_tables_to_plurals.py @@ -8,7 +8,7 @@ import re from alembic import op -from sqlalchemy import MetaData, create_engine +from sqlalchemy import MetaData, create_engine, inspect from sqlalchemy.engine import reflection # revision identifiers, used by Alembic. from sqlalchemy.engine.reflection import Inspector @@ -72,10 +72,10 @@ def upgrade(): """ conn = op.get_bind() - inspector = Inspector.from_engine(conn) + inspector = inspect(conn) tables = inspector.get_table_names() - metadata = MetaData(conn) - metadata.reflect() + metadata = MetaData() + metadata.reflect(conn) table: str for table in tables: @@ -88,10 +88,10 @@ def upgrade(): def downgrade(): conn = op.get_bind() - inspector = Inspector.from_engine(conn) + inspector = inspect(conn) tables = inspector.get_table_names() - metadata = MetaData(conn) - metadata.reflect() + metadata = MetaData() + metadata.reflect(conn) table_mapping_reversed = {y: x for x, y in table_mapping.items()} table: str for table in tables: diff --git a/pay-api/migrations/versions/b7443d501d98_entity_fee_codes.py b/pay-api/migrations/versions/b7443d501d98_entity_fee_codes.py index f1b6bf547..44d37f814 100644 --- a/pay-api/migrations/versions/b7443d501d98_entity_fee_codes.py +++ b/pay-api/migrations/versions/b7443d501d98_entity_fee_codes.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, Float, String +from sqlalchemy import Date, Float, String, text from sqlalchemy.sql import column, table @@ -37,7 +37,7 @@ def upgrade(): ) # Patch to adjust manual code entries in other dev/test/prod conn = op.get_bind() - res = conn.execute("select * from corp_type where code='BEN'") + res = conn.execute(text("select * from corp_type where code='BEN'")) ben_code = res.fetchall() if len(ben_code) == 0: # Insert BEN diff --git a/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py b/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py index 9c9290dde..6de5707af 100644 --- a/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py +++ b/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, String, Boolean +from sqlalchemy import Date, String, Boolean, text, Integer from sqlalchemy.sql import column, table # revision identifiers, used by Alembic. @@ -29,8 +29,8 @@ def upgrade(): column('bcol_staff_fee_code', String) ) distribution_code_link_table = table('distribution_code_links', - column('distribution_code_id', String), - column('fee_schedule_id', String) + column('distribution_code_id', Integer), + column('fee_schedule_id', Integer) ) fee_schedule_table = table('fee_schedules', column('filing_type_code', String), @@ -111,25 +111,26 @@ def upgrade(): ] ) # Now find out the distribution code for other BCINC and map it to them. - distribution_code_id_query = "select dc.distribution_code_id from fee_schedules fs " \ - "left join distribution_code_links dcl on fs.fee_schedule_id=dcl.fee_schedule_id " \ - "left join distribution_codes dc on dc.distribution_code_id = dcl.distribution_code_id " \ - "where fs.filing_type_code = 'BCINC' and dc.start_date <= CURRENT_DATE " \ - "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" - conn = op.get_bind() - res = conn.execute(distribution_code_id_query) - distribution_code_id = res.fetchall()[0][0] - new_codes = ('CCC', 'ULC', 'LTD', 'BC') - distr_code_link_values = [] - for new_code in new_codes: - res = conn.execute( - f"select fee_schedule_id from fee_schedules where corp_type_code='{new_code}' and filing_type_code='BCINC'") - fee_schedule_id = res.fetchall()[0][0] - distr_code_link_values.append({ - 'distribution_code_id': distribution_code_id, - 'fee_schedule_id': fee_schedule_id - }) - op.bulk_insert(distribution_code_link_table, distr_code_link_values) + # Causing errors: + # distribution_code_id_query = "select dc.distribution_code_id from fee_schedules fs " \ + # "left join distribution_code_links dcl on fs.fee_schedule_id=dcl.fee_schedule_id " \ + # "left join distribution_codes dc on dc.distribution_code_id = dcl.distribution_code_id " \ + # "where fs.filing_type_code = 'BCINC' and dc.start_date <= CURRENT_DATE " \ + # "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" + # conn = op.get_bind() + # res = conn.execute(text(distribution_code_id_query)) + # distribution_code_id = res.fetchall()[0][0] + # new_codes = ('CCC', 'ULC', 'LTD', 'BC') + # distr_code_link_values = [] + # for new_code in new_codes: + # res = conn.execute( + # text(f"select fee_schedule_id from fee_schedules where corp_type_code='{new_code}' and filing_type_code='BCINC'")) + # fee_schedule_id = res.fetchall()[0][0] + # distr_code_link_values.append({ + # 'distribution_code_id': distribution_code_id, + # 'fee_schedule_id': fee_schedule_id + # }) + # op.bulk_insert(distribution_code_link_table, distr_code_link_values) # ### end Alembic commands ### diff --git a/pay-api/migrations/versions/c871202927f0_rush_fee_code.py b/pay-api/migrations/versions/c871202927f0_rush_fee_code.py index c67c6789b..98519de3a 100644 --- a/pay-api/migrations/versions/c871202927f0_rush_fee_code.py +++ b/pay-api/migrations/versions/c871202927f0_rush_fee_code.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, String, Float +from sqlalchemy import Date, String, Float, text from sqlalchemy.sql import column, table # revision identifiers, used by Alembic. @@ -76,11 +76,11 @@ def upgrade(): "where upper(dc.name) = 'VITAL STATISTICS' and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - f"select fee_schedule_id from fee_schedules where corp_type_code='VS' and filing_type_code='WILLRUSH'") + text(f"select fee_schedule_id from fee_schedules where corp_type_code='VS' and filing_type_code='WILLRUSH'")) fee_schedule_id = res.fetchall()[0][0] distr_code_link = [{ 'distribution_code_id': distribution_code_id, diff --git a/pay-api/migrations/versions/dbe9dc38ac33_firms_fee_codes.py b/pay-api/migrations/versions/dbe9dc38ac33_firms_fee_codes.py index e10a65566..7e1fc5a6f 100644 --- a/pay-api/migrations/versions/dbe9dc38ac33_firms_fee_codes.py +++ b/pay-api/migrations/versions/dbe9dc38ac33_firms_fee_codes.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, String +from sqlalchemy import Date, String, text from sqlalchemy.sql import column, table # revision identifiers, used by Alembic. @@ -66,11 +66,11 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - f"select fee_schedule_id from fee_schedules where filing_type_code in ('DIS_VOL') and corp_type_code in ('SP','GP') ") + text(f"select fee_schedule_id from fee_schedules where filing_type_code in ('DIS_VOL') and corp_type_code in ('SP','GP') ")) distr_code_links = [] for result in res.fetchall(): fee_schedule_id = result[0] diff --git a/pay-api/migrations/versions/e748b5c19247_.py b/pay-api/migrations/versions/e748b5c19247_.py index 3a388cb93..7f60b7356 100644 --- a/pay-api/migrations/versions/e748b5c19247_.py +++ b/pay-api/migrations/versions/e748b5c19247_.py @@ -10,7 +10,7 @@ from alembic import op import sqlalchemy as sa -from sqlalchemy import Date, Float, Integer, Boolean, String +from sqlalchemy import Date, Integer, Boolean, String from sqlalchemy.sql import column, table @@ -56,8 +56,8 @@ def upgrade(): ) distribution_code_link_table = table('distribution_code_links', - column('distribution_code_id', String), - column('fee_schedule_id', String) + column('distribution_code_id', Integer), + column('fee_schedule_id', Integer) ) # Product code/corp type @@ -130,13 +130,13 @@ def upgrade(): distribution_code_id_query = "select distribution_code_id from distribution_codes where name = 'BC Assessment' and created_by = 'Alembic'" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(sa.text(distribution_code_id_query)) distribution_code_id = res.fetchall()[0][0] new_codes = ('OLAARTOQ', 'OLAARTAQ', 'OLAARTIQ') distr_code_link_values = [] for new_code in new_codes: res = conn.execute( - f"select fee_schedule_id from fee_schedules where filing_type_code='{new_code}' and corp_type_code='BCA'") + sa.text(f"select fee_schedule_id from fee_schedules where filing_type_code='{new_code}' and corp_type_code='BCA'")) fee_schedule_id = res.fetchall()[0][0] distr_code_link_values.append({ 'distribution_code_id': distribution_code_id, diff --git a/pay-api/openshift/templates/dc.json b/pay-api/openshift/templates/dc.json deleted file mode 100644 index f69e36efe..000000000 --- a/pay-api/openshift/templates/dc.json +++ /dev/null @@ -1,368 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for a pay api service.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%", - "pre": { - "failurePolicy": "Abort", - "execNewPod": { - "command": [ - "/opt/app-root/src/pre-hook-update-db.sh" - ], - "env": [ - { - "name": "DATABASE_ADMIN_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${NAME}-${TAG_NAME}-secret", - "key": "DATABASE_ADMIN_PASSWORD" - } - } - }, - { - "name": "DATABASE_USERNAME", - "valueFrom": { - "secretKeyRef": { - "name": "${NAME}-${TAG_NAME}-secret", - "key": "DATABASE_USER" - } - } - }, - { - "name": "DATABASE_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${NAME}-${TAG_NAME}-secret", - "key": "DATABASE_PASSWORD" - } - } - }, - { - "name": "DATABASE_NAME", - "valueFrom": { - "secretKeyRef": { - "name": "${NAME}-${TAG_NAME}-secret", - "key": "DATABASE_NAME" - } - } - }, - { - "name": "DATABASE_HOST", - "valueFrom": { - "secretKeyRef": { - "name": "${NAME}-${TAG_NAME}-secret", - "key": "DATABASE_HOST" - } - } - }, - { - "name": "DATABASE_PORT", - "valueFrom": { - "secretKeyRef": { - "name": "${NAME}-${TAG_NAME}-secret", - "key": "DATABASE_PORT" - } - } - } - ], - "containerName": "${NAME}-${TAG_NAME}" - } - } - } - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "${IMAGE_NAMESPACE}", - "name": "${NAME}:${TAG_NAME}" - } - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": "${REPLICAS}", - "test": false, - "selector": { - "app": "${NAME}-${TAG_NAME}", - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "template": { - "metadata": { - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "deploymentconfig": "${NAME}-${TAG_NAME}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "containers": [ - { - "name": "${NAME}-${TAG_NAME}", - "image": "docker-registry.default.svc:5000/${IMAGE_NAMESPACE}/${NAME}:${TAG_NAME}", - "ports": [ - { - "containerPort": 8080, - "protocol": "TCP" - } - ], - "resources": { - "requests": { - "cpu": "${CPU_REQUEST}", - "memory": "${MEMORY_REQUEST}" - }, - "limits": { - "cpu": "${CPU_LIMIT}", - "memory": "${MEMORY_LIMIT}" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/ops/healthz", - "port": 8080, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "readinessProbe": { - "httpGet": { - "path": "/ops/readyz", - "port": 8080, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - }, - { - "name": "jaeger-agent", - "image": "jaegertracing/jaeger-agent", - "ports": [ - { - "containerPort": 5775, - "protocol": "UDP" - }, - { - "containerPort": 5778, - "protocol": "UDP" - }, - { - "containerPort": 6831, - "protocol": "UDP" - }, - { - "containerPort": 6832, - "protocol": "UDP" - } - ], - "args": [ - "${JAEGER_COLLECTOR}" - ] - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - } - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "creationTimestamp": null, - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "ports": [ - { - "name": "8080-tcp", - "protocol": "TCP", - "port": 8080, - "targetPort": 8080 - } - ], - "selector": { - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - }, - { - "kind": "Route", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "host": "${NAME}-${TAG_NAME}.pathfinder.gov.bc.ca", - "to": { - "kind": "Service", - "name": "${NAME}-${TAG_NAME}", - "weight": 100 - }, - "port": { - "targetPort": "8080-tcp" - }, - "tls": { - "termination": "edge" - }, - "wildcardPolicy": "None" - }, - "status": { - "ingress": [ - { - "host": "${NAME}-${TAG_NAME}.pathfinder.gov.bc.ca", - "routerName": "router", - "conditions": [ - { - "type": "Admitted", - "status": "True" - } - ], - "wildcardPolicy": "None" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "pay-api" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - }, - { - "name": "JAEGER_COLLECTOR", - "displayName": "Jaeger Tracing collector address", - "description": "Jaeger Tracing collector address.", - "required": true, - "value": "--collector.host-port=jaeger-collector.d7eovc-${TAG_NAME}.svc:14267" - } - ] -} diff --git a/pay-api/openshift/templates/pay-api-build.json b/pay-api/openshift/templates/pay-api-build.json deleted file mode 100644 index fd0f06445..000000000 --- a/pay-api/openshift/templates/pay-api-build.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Build template for a pay-api service.", - "tags": "flask", - "iconClass": "icon-python" - }, - "name": "${NAME}-build" - }, - "objects": [ - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}" - } - }, - { - "kind": "BuildConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}", - "labels": { - "app": "${NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-build" - } - }, - "spec": { - "source": { - "type": "Git", - "git": { - "uri": "${GIT_REPO_URL}", - "ref": "${GIT_REF}" - }, - "contextDir": "${SOURCE_CONTEXT_DIR}" - }, - "strategy": { - "type": "Docker", - "dockerStrategy": { - "dockerfilePath": "${DOCKER_FILE_PATH}" - } - }, - "output": { - "to": { - "kind": "ImageStreamTag", - "name": "${NAME}:${OUTPUT_IMAGE_TAG}" - } - }, - "triggers": [ - { - "type": "ConfigChange" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the objects defined in this template. You should keep this as default unless your know what your doing.", - "required": true, - "value": "pay-api" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "GIT_REPO_URL", - "displayName": "Git Repo URL", - "description": "The URL to your GIT repo, don't use the this default unless your just experimenting.", - "required": true, - "value": "https://github.com/bcgov/sbc-pay.git" - }, - { - "name": "GIT_REF", - "displayName": "Git Reference", - "description": "The git reference or branch.", - "required": true, - "value": "development" - }, - { - "name": "SOURCE_CONTEXT_DIR", - "displayName": "Source Context Directory", - "description": "The source context directory.", - "required": true, - "value": "pay-api" - }, - { - "name": "OUTPUT_IMAGE_TAG", - "displayName": "Output Image Tag", - "description": "The tag given to the built image.", - "required": true, - "value": "latest" - }, - { - "name": "DOCKER_FILE_PATH", - "displayName": "Docker File Path", - "description": "The path to the docker file defining the build.", - "required": false, - "value": "Dockerfile" - } - ] -} diff --git a/pay-api/openshift/templates/pay-api-deploy.json b/pay-api/openshift/templates/pay-api-deploy.json deleted file mode 100644 index 895623d11..000000000 --- a/pay-api/openshift/templates/pay-api-deploy.json +++ /dev/null @@ -1,360 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for a pay api service.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%", - "pre": { - "failurePolicy": "Abort", - "execNewPod": { - "command": [ - "/opt/app-root/pre-hook-update-db.sh" - ], - "env": [ - { - "name": "DATABASE_USERNAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_USER" - } - } - }, - { - "name": "DATABASE_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-secret", - "key": "DATABASE_PASSWORD" - } - } - }, - { - "name": "DATABASE_NAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_NAME" - } - } - }, - { - "name": "DATABASE_HOST", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_HOST" - } - } - }, - { - "name": "DATABASE_PORT", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_PORT" - } - } - } - ], - "containerName": "${NAME}-${TAG_NAME}" - } - } - } - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "${IMAGE_NAMESPACE}", - "name": "${NAME}:${TAG_NAME}" - } - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": "${REPLICAS}", - "test": false, - "selector": { - "app": "${NAME}-${TAG_NAME}", - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "template": { - "metadata": { - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "deploymentconfig": "${NAME}-${TAG_NAME}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "containers": [ - { - "name": "${NAME}-${TAG_NAME}", - "image": "docker-registry.default.svc:5000/${IMAGE_NAMESPACE}/${NAME}:${TAG_NAME}", - "ports": [ - { - "containerPort": 8080, - "protocol": "TCP" - } - ], - "env": [], - "resources": { - "requests": { - "cpu": "${CPU_REQUEST}", - "memory": "${MEMORY_REQUEST}" - }, - "limits": { - "cpu": "${CPU_LIMIT}", - "memory": "${MEMORY_LIMIT}" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/ops/healthz", - "port": 8080, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "readinessProbe": { - "httpGet": { - "path": "/ops/readyz", - "port": 8080, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - }, - { - "name": "jaeger-agent", - "image": "jaegertracing/jaeger-agent", - "ports": [ - { - "containerPort": 5775, - "protocol": "UDP" - }, - { - "containerPort": 5778, - "protocol": "UDP" - }, - { - "containerPort": 6831, - "protocol": "UDP" - }, - { - "containerPort": 6832, - "protocol": "UDP" - } - ], - "args": [ - "${JAEGER_COLLECTOR}" - ] - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - } - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "creationTimestamp": null, - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "ports": [ - { - "name": "8080-tcp", - "protocol": "TCP", - "port": 8080, - "targetPort": 8080 - } - ], - "selector": { - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - }, - { - "kind": "Route", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "host": "${NAME}-${TAG_NAME}.pathfinder.gov.bc.ca", - "to": { - "kind": "Service", - "name": "${NAME}-${TAG_NAME}", - "weight": 100 - }, - "port": { - "targetPort": "8080-tcp" - }, - "tls": { - "termination": "edge" - }, - "wildcardPolicy": "None" - }, - "status": { - "ingress": [ - { - "host": "${NAME}-${TAG_NAME}.pathfinder.gov.bc.ca", - "routerName": "router", - "conditions": [ - { - "type": "Admitted", - "status": "True" - } - ], - "wildcardPolicy": "None" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "pay-api" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - }, - { - "name": "JAEGER_COLLECTOR", - "displayName": "Jaeger Tracing collector address", - "description": "Jaeger Tracing collector address.", - "required": true, - "value": "--collector.host-port=jaeger-collector.d7eovc-${TAG_NAME}.svc:14267" - } - ] -} diff --git a/pay-api/poetry.lock b/pay-api/poetry.lock new file mode 100644 index 000000000..3253cd826 --- /dev/null +++ b/pay-api/poetry.lock @@ -0,0 +1,2366 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + +[[package]] +name = "astroid" +version = "3.2.2" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, + {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "autopep8" +version = "2.2.0" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.8" +files = [ + {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"}, + {file = "autopep8-2.2.0.tar.gz", hash = "sha256:d306a0581163ac29908280ad557773a95a9bede072c0fafed6f141f5311f43c1"}, +] + +[package.dependencies] +pycodestyle = ">=2.11.0" + +[[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 = "cachelib" +version = "0.9.0" +description = "A collection of cache libraries in the same API interface." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachelib-0.9.0-py3-none-any.whl", hash = "sha256:811ceeb1209d2fe51cd2b62810bd1eccf70feba5c52641532498be5c675493b3"}, + {file = "cachelib-0.9.0.tar.gz", hash = "sha256:38222cc7c1b79a23606de5c2607f4925779e37cdcea1c2ad21b8bae94b5425a5"}, +] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "cattrs" +version = "23.2.3" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"}, + {file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"}, +] + +[package.dependencies] +attrs = ">=23.1.0" + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +orjson = ["orjson (>=3.9.2)"] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.5.3" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "croniter" +version = "2.0.2" +description = "croniter provides iteration for datetime object with cron like format" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "croniter-2.0.2-py2.py3-none-any.whl", hash = "sha256:78bf110a2c7dbbfdd98b926318ae6c64a731a4c637c7befe3685755110834746"}, + {file = "croniter-2.0.2.tar.gz", hash = "sha256:8bff16c9af4ef1fb6f05416973b8f7cb54997c02f2f8365251f9bf1dded91866"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = ">2021.1" + +[[package]] +name = "cryptography" +version = "42.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "dpath" +version = "2.1.6" +description = "Filesystem-like pathing and searching for dictionaries" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dpath-2.1.6-py3-none-any.whl", hash = "sha256:31407395b177ab63ef72e2f6ae268c15e938f2990a8ecf6510f5686c02b6db73"}, + {file = "dpath-2.1.6.tar.gz", hash = "sha256:f1e07c72e8605c6a9e80b64bc8f42714de08a789c7de417e49c3f87a19692e47"}, +] + +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "expiringdict" +version = "1.2.2" +description = "Dictionary with auto-expiring values for caching purposes" +optional = false +python-versions = "*" +files = [ + {file = "expiringdict-1.2.2-py3-none-any.whl", hash = "sha256:09a5d20bc361163e6432a874edd3179676e935eb81b925eccef48d409a8a45e8"}, + {file = "expiringdict-1.2.2.tar.gz", hash = "sha256:300fb92a7e98f15b05cf9a856c1415b3bc4f2e132be07daa326da6414c23ee09"}, +] + +[package.extras] +tests = ["coverage", "coveralls", "dill", "mock", "nose"] + +[[package]] +name = "faker" +version = "24.14.1" +description = "Faker is a Python package that generates fake data for you." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Faker-24.14.1-py3-none-any.whl", hash = "sha256:a5edba3aa17a1d689c8907e5b0cd1653079c2466a4807f083aa7b5f80a00225d"}, + {file = "Faker-24.14.1.tar.gz", hash = "sha256:380a3697e696ae4fcf50a93a3d9e0286fab7dfbf05a9caa4421fa4727c6b1e89"}, +] + +[package.dependencies] +python-dateutil = ">=2.4" + +[[package]] +name = "flake8" +version = "7.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "flake8-blind-except" +version = "0.2.1" +description = "A flake8 extension that checks for blind except: statements" +optional = false +python-versions = "*" +files = [ + {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, +] + +[[package]] +name = "flake8-debugger" +version = "4.1.2" +description = "ipdb/pdb statement checker plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-isort" +version = "6.1.1" +description = "flake8 plugin that integrates isort" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_isort-6.1.1-py3-none-any.whl", hash = "sha256:0fec4dc3a15aefbdbe4012e51d5531a2eb5fa8b981cdfbc882296a59b54ede12"}, + {file = "flake8_isort-6.1.1.tar.gz", hash = "sha256:c1f82f3cf06a80c13e1d09bfae460e9666255d5c780b859f19f8318d420370b3"}, +] + +[package.dependencies] +flake8 = "*" +isort = ">=5.0.0,<6" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "flake8-quotes" +version = "3.4.0" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, +] + +[package.dependencies] +flake8 = "*" +setuptools = "*" + +[[package]] +name = "flask" +version = "3.0.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, +] + +[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 = "flask-caching" +version = "2.3.0" +description = "Adds caching support to Flask applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, + {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, +] + +[package.dependencies] +cachelib = ">=0.9.0,<0.10.0" +Flask = "*" + +[[package]] +name = "flask-cors" +version = "4.0.0" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Cors-4.0.0.tar.gz", hash = "sha256:f268522fcb2f73e2ecdde1ef45e2fd5c71cc48fe03cffb4b441c6d1b40684eb0"}, + {file = "Flask_Cors-4.0.0-py2.py3-none-any.whl", hash = "sha256:bc3492bfd6368d27cfe79c7821df5a8a319e1a6d5eab277a3794be19bdc51783"}, +] + +[package.dependencies] +Flask = ">=0.9" + +[[package]] +name = "flask-jwt-oidc" +version = "0.7.0" +description = "Opinionated flask oidc client" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +cachelib = "0.*" +Flask = ">=2" +python-jose = "^3.3.0" +six = "^1.16.0" + +[package.source] +type = "git" +url = "https://github.com/seeker25/flask-jwt-oidc.git" +reference = "HEAD" +resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" + +[[package]] +name = "flask-marshmallow" +version = "1.2.0" +description = "Flask + marshmallow for beautiful APIs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_marshmallow-1.2.0-py3-none-any.whl", hash = "sha256:ddd2a7c8db5e00a8d56c8ca5f651efae1de7d76b7d821b56ccc2caf09135ad12"}, + {file = "flask_marshmallow-1.2.0.tar.gz", hash = "sha256:d0f79eb9743f0c530a3d9e848503e1f2228e6b35a819c91e913af02e68421805"}, +] + +[package.dependencies] +Flask = ">=2.2" +marshmallow = ">=3.0.0" + +[package.extras] +dev = ["flask-marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["Sphinx (==7.2.6)", "marshmallow-sqlalchemy (>=0.19.0)", "sphinx-issues (==4.0.0)"] +sqlalchemy = ["flask-sqlalchemy (>=3.0.0)", "marshmallow-sqlalchemy (>=0.29.0)"] +tests = ["flask-marshmallow[sqlalchemy]", "pytest"] + +[[package]] +name = "flask-migrate" +version = "4.0.7" +description = "SQLAlchemy database migrations for Flask applications using Alembic." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Migrate-4.0.7.tar.gz", hash = "sha256:dff7dd25113c210b069af280ea713b883f3840c1e3455274745d7355778c8622"}, + {file = "Flask_Migrate-4.0.7-py3-none-any.whl", hash = "sha256:5c532be17e7b43a223b7500d620edae33795df27c75811ddf32560f7d48ec617"}, +] + +[package.dependencies] +alembic = ">=1.9.0" +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" + +[[package]] +name = "flask-moment" +version = "1.0.5" +description = "Formatting of dates and times in Flask templates using moment.js." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Moment-1.0.5.tar.gz", hash = "sha256:33307ecd4af8290b6df6a9828ff55ac0977d0567817f9bc0f69803d22ed2b55c"}, + {file = "Flask_Moment-1.0.5-py3-none-any.whl", hash = "sha256:6e7b3eef89e2137bbbee975405f241a68a44edfa34bf052c92d84364992adca6"}, +] + +[package.dependencies] +Flask = "*" +packaging = ">=14.1" + +[[package]] +name = "flask-opentracing" +version = "1.1.0" +description = "OpenTracing support for Flask applications" +optional = false +python-versions = "*" +files = [ + {file = "Flask-OpenTracing-1.1.0.tar.gz", hash = "sha256:a9a39d367fbe7e9ed9c77b90ac48159c1a3e82982a5abf84d3f4d710d24580ac"}, +] + +[package.dependencies] +Flask = "*" +opentracing = ">=2.0,<3" + +[package.extras] +tests = ["flake8", "flake8-quotes", "mock", "pytest", "pytest-cov"] + +[[package]] +name = "flask-script" +version = "2.0.6" +description = "Scripting support for Flask" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Script-2.0.6.tar.gz", hash = "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65"}, +] + +[package.dependencies] +Flask = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +description = "Add SQLAlchemy support to your Flask application." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[package.dependencies] +flask = ">=2.2.5" +sqlalchemy = ">=2.0.16" + +[[package]] +name = "freezegun" +version = "1.5.1" +description = "Let your Python tests travel through time" +optional = false +python-versions = ">=3.7" +files = [ + {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, + {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + +[[package]] +name = "gcp-queue" +version = "0.3.0" +description = "" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +flask = ">=1" +google-auth = "^2.28.2" +google-cloud-pubsub = "^2.20.2" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-connect-common.git" +reference = "main" +resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" +subdirectory = "python/gcp-queue" + +[[package]] +name = "google-api-core" +version = "2.19.0" +description = "Google API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, + {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +proto-plus = ">=1.22.3,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-auth" +version = "2.29.0" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-cloud-pubsub" +version = "2.21.2" +description = "Google Cloud Pub/Sub API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, + {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<3.0.0dev" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +grpcio = ">=1.51.3,<2.0dev" +grpcio-status = ">=1.33.2" +proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +libcst = ["libcst (>=0.3.10)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.63.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, + {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.13.0" +description = "IAM API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.13.0.tar.gz", hash = "sha256:fad318608b9e093258fbf12529180f400d1c44453698a33509cc6ecf005b294e"}, + {file = "grpc_google_iam_v1-0.13.0-py2.py3-none-any.whl", hash = "sha256:53902e2af7de8df8c1bd91373d9be55b0743ec267a7428ea638db3775becae89"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.64.0" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.8" +files = [ + {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, + {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, + {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, + {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, + {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, + {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, + {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, + {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, + {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, + {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, + {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, + {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, + {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, + {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, + {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, + {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, + {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, + {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, + {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, + {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, + {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.64.0)"] + +[[package]] +name = "grpcio-status" +version = "1.62.2" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, + {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.62.2" +protobuf = ">=4.21.6" + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "holidays" +version = "0.37" +description = "Generate and work with holidays in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "holidays-0.37-py3-none-any.whl", hash = "sha256:5b8ff8c94c06e3b225762d495e51b8e51205d332f8ad092aab809c4bffa8d123"}, + {file = "holidays-0.37.tar.gz", hash = "sha256:712df71a8d97b04554fa1c9208d219fbf174bad2864263bef24c6dcfa1ded6ff"}, +] + +[package.dependencies] +python-dateutil = "*" + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[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 = "jaeger-client" +version = "4.8.0" +description = "Jaeger Python OpenTracing Tracer implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaeger-client-4.8.0.tar.gz", hash = "sha256:3157836edab8e2c209bd2d6ae61113db36f7ee399e66b1dcbb715d87ab49bfe0"}, +] + +[package.dependencies] +opentracing = ">=2.1,<3.0" +threadloop = ">=1,<2" +thrift = "*" +tornado = ">=4.3" + +[package.extras] +tests = ["codecov", "coverage", "flake8", "flake8-quotes", "flake8-typing-imports", "mock", "mypy", "opentracing_instrumentation (>=3,<4)", "prometheus_client (==0.11.0)", "pycurl", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "pytest-localserver", "pytest-timeout", "pytest-tornado", "tchannel (==2.1.0)"] + +[[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 = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "launchdarkly-eventsource" +version = "1.1.1" +description = "LaunchDarkly SSE Client" +optional = false +python-versions = ">=3.7" +files = [ + {file = "launchdarkly_eventsource-1.1.1-py3-none-any.whl", hash = "sha256:3d7e5301bc4b4a744ecdaa10de8bce52c2f3c66a97e9aa10ab11ca81b67fb31b"}, + {file = "launchdarkly_eventsource-1.1.1.tar.gz", hash = "sha256:211791f1267f9b7b0a62a0bb5fc9c5ed1fb4a834440f16be551968dbe772557a"}, +] + +[package.dependencies] +urllib3 = ">=1.26.0,<3" + +[[package]] +name = "launchdarkly-server-sdk" +version = "8.2.1" +description = "LaunchDarkly SDK for Python" +optional = false +python-versions = "*" +files = [ + {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, + {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, +] + +[package.dependencies] +certifi = ">=2018.4.16" +expiringdict = ">=1.1.4" +pyRFC3339 = ">=1.0" +semver = ">=2.10.2" +urllib3 = ">=1.22.0,<3" + +[package.extras] +consul = ["python-consul (>=1.0.1)"] +dynamodb = ["boto3 (>=1.9.71)"] +redis = ["redis (>=2.10.5)"] + +[[package]] +name = "lovely-pytest-docker" +version = "0.3.1" +description = "Pytest testing utilities with docker containers." +optional = false +python-versions = "*" +files = [ + {file = "lovely-pytest-docker-0.3.1.tar.gz", hash = "sha256:4326a180bfd4dd4ad69c2ef3e3643c41075d965f40068488b40204602e6df85e"}, +] + +[package.dependencies] +pytest = "*" +six = "*" + +[[package]] +name = "mako" +version = "1.3.2" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, + {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "marshmallow" +version = "3.21.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-sqlalchemy" +version = "1.0.0" +description = "SQLAlchemy integration with the marshmallow (de)serialization library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow_sqlalchemy-1.0.0-py3-none-any.whl", hash = "sha256:f415d57809e3555b6323356589aba91e36e4470f35953d3a10c755ac5c3307df"}, + {file = "marshmallow_sqlalchemy-1.0.0.tar.gz", hash = "sha256:20a0f2fcdd5bddc86444fa01461f17f9b6a12a8ddd4ca8c9b34fe2f2e35d00a2"}, +] + +[package.dependencies] +marshmallow = ">=3.10.0" +SQLAlchemy = ">=1.4.40,<3.0" + +[package.extras] +dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)"] +tests = ["pytest (<8)", "pytest-lazy-fixture (>=0.6.2)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "opentracing" +version = "2.4.0" +description = "OpenTracing API for Python. See documentation at http://opentracing.io" +optional = false +python-versions = "*" +files = [ + {file = "opentracing-2.4.0.tar.gz", hash = "sha256:a173117e6ef580d55874734d1fa7ecb6f3655160b8b8974a2a1e98e5ec9c840d"}, +] + +[package.extras] +tests = ["Sphinx", "doubles", "flake8", "flake8-quotes", "gevent", "mock", "pytest", "pytest-cov", "pytest-mock", "six (>=1.10.0,<2.0)", "sphinx_rtd_theme", "tornado"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "pg8000" +version = "1.31.2" +description = "PostgreSQL interface library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, + {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.2" +scramp = ">=1.4.5" + +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "proto-plus" +version = "1.23.0" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.23.0.tar.gz", hash = "sha256:89075171ef11988b3fa157f5dbd8b9cf09d65fffee97e29ce403cd8defba19d2"}, + {file = "proto_plus-1.23.0-py3-none-any.whl", hash = "sha256:a829c79e619e1cf632de091013a4173deed13a55f326ef84f05af6f50ff4c82c"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + +[[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 = "pyasn1" +version = "0.5.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pyhamcrest" +version = "2.1.0" +description = "Hamcrest framework for matcher objects" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyhamcrest-2.1.0-py3-none-any.whl", hash = "sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587"}, + {file = "pyhamcrest-2.1.0.tar.gz", hash = "sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c"}, +] + +[package.extras] +dev = ["black", "doc2dash", "flake8", "pyhamcrest[docs,tests]", "pytest-mypy", "towncrier", "tox", "tox-asdf", "twine"] +docs = ["alabaster (>=0.7,<1.0)", "sphinx (>=4.0,<5.0)"] +tests = ["coverage[toml]", "dataclasses", "mypy (!=0.940)", "pytest (>=5.0)", "pytest-mypy-plugins", "pytest-sugar", "pytest-xdist", "pyyaml", "types-dataclasses", "types-mock"] +tests-numpy = ["numpy", "pyhamcrest[tests]"] + +[[package]] +name = "pyhumps" +version = "3.8.0" +description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" +optional = false +python-versions = "*" +files = [ + {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, + {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, +] + +[[package]] +name = "pylint" +version = "3.2.2" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, + {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, +] + +[package.dependencies] +astroid = ">=3.2.2,<=3.3.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint-flask" +version = "0.6" +description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" +optional = false +python-versions = "*" +files = [ + {file = "pylint-flask-0.6.tar.gz", hash = "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517"}, +] + +[package.dependencies] +pylint-plugin-utils = ">=0.2.1" + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + +[[package]] +name = "pyrfc3339" +version = "1.1" +description = "Generate and parse RFC 3339 timestamps" +optional = false +python-versions = "*" +files = [ + {file = "pyRFC3339-1.1-py2.py3-none-any.whl", hash = "sha256:67196cb83b470709c580bb4738b83165e67c6cc60e1f2e4f286cfcb402a926f4"}, + {file = "pyRFC3339-1.1.tar.gz", hash = "sha256:81b8cbe1519cdb79bed04910dd6fa4e181faf8c88dff1e1b987b5f7ab23a5b1a"}, +] + +[package.dependencies] +pytz = "*" + +[[package]] +name = "pyrsistent" +version = "0.20.0" +description = "Persistent/Functional/Immutable data structures" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, + {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, + {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, + {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, + {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, + {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, + {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, +] + +[[package]] +name = "pytest" +version = "8.2.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, + {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, +] + +[package.dependencies] +pytest = ">=6.2.5" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +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.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "sbc_common_components" +version = "0.0.0" +description = "" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +flask = "*" +flask-jwt-oidc = ">=0.1.5" +Flask-OpenTracing = "1.1.0" +Flask-SQLAlchemy = "*" +jaeger-client = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-common-components.git" +reference = "HEAD" +resolved_reference = "e770b4ab496e044d292500e62bc19a17079a73ec" +subdirectory = "python" + +[[package]] +name = "scramp" +version = "1.4.5" +description = "An implementation of the SCRAM protocol." +optional = false +python-versions = ">=3.8" +files = [ + {file = "scramp-1.4.5-py3-none-any.whl", hash = "sha256:50e37c464fc67f37994e35bee4151e3d8f9320e9c204fca83a5d313c121bbbe7"}, + {file = "scramp-1.4.5.tar.gz", hash = "sha256:be3fbe774ca577a7a658117dca014e5d254d158cecae3dd60332dfe33ce6d78e"}, +] + +[package.dependencies] +asn1crypto = ">=1.5.1" + +[[package]] +name = "semver" +version = "3.0.2" +description = "Python helper for Semantic Versioning (https://semver.org)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "semver-3.0.2-py3-none-any.whl", hash = "sha256:b1ea4686fe70b981f85359eda33199d60c53964284e0cfb4977d243e37cf4bf4"}, + {file = "semver-3.0.2.tar.gz", hash = "sha256:6253adb39c70f6e51afed2fa7152bcd414c411286088fb4b9effb133885ab4cc"}, +] + +[[package]] +name = "sentry-sdk" +version = "1.41.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.41.0.tar.gz", hash = "sha256:4f2d6c43c07925d8cd10dfbd0970ea7cb784f70e79523cca9dbcd72df38e5a46"}, + {file = "sentry_sdk-1.41.0-py2.py3-none-any.whl", hash = "sha256:be4f8f4b29a80b6a3b71f0f31487beb9e296391da20af8504498a328befed53f"}, +] + +[package.dependencies] +certifi = "*" +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "70.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "simple-cloudevent" +version = "0.0.2" +description = "A short description of the project" +optional = false +python-versions = ">=3.8" +files = [] +develop = false + +[package.dependencies] +strict-rfc3339 = "*" + +[package.source] +type = "git" +url = "https://github.com/daxiom/simple-cloudevent.py.git" +reference = "HEAD" +resolved_reference = "447cabb988202206ac69e71177d7cd11b6c0b002" + +[[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 = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sql-versioning" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.10" +files = [] +develop = false + +[package.source] +type = "git" +url = "https://github.com/bcgov/lear.git" +reference = "feature-legal-name" +resolved_reference = "e5a432d1460dc84208465ef35c0c81ab02e66f51" +subdirectory = "python/common/sql-versioning" + +[[package]] +name = "sqlalchemy" +version = "2.0.28" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "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\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlalchemy-utils" +version = "0.41.1" +description = "Various utility functions for SQLAlchemy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "SQLAlchemy-Utils-0.41.1.tar.gz", hash = "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74"}, + {file = "SQLAlchemy_Utils-0.41.1-py3-none-any.whl", hash = "sha256:6c96b0768ea3f15c0dc56b363d386138c562752b84f647fb8d31a2223aaab801"}, +] + +[package.dependencies] +SQLAlchemy = ">=1.3" + +[package.extras] +arrow = ["arrow (>=0.3.4)"] +babel = ["Babel (>=1.3)"] +color = ["colour (>=0.0.4)"] +encrypted = ["cryptography (>=0.6)"] +intervals = ["intervals (>=0.7.1)"] +password = ["passlib (>=1.6,<2.0)"] +pendulum = ["pendulum (>=2.0.5)"] +phone = ["phonenumbers (>=5.9.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +timezone = ["python-dateutil"] +url = ["furl (>=0.4.1)"] + +[[package]] +name = "strict-rfc3339" +version = "0.7" +description = "Strict, simple, lightweight RFC3339 functions" +optional = false +python-versions = "*" +files = [ + {file = "strict-rfc3339-0.7.tar.gz", hash = "sha256:5cad17bedfc3af57b399db0fed32771f18fc54bbd917e85546088607ac5e1277"}, +] + +[[package]] +name = "threadloop" +version = "1.0.2" +description = "Tornado IOLoop Backed Concurrent Futures" +optional = false +python-versions = "*" +files = [ + {file = "threadloop-1.0.2-py2-none-any.whl", hash = "sha256:5c90dbefab6ffbdba26afb4829d2a9df8275d13ac7dc58dccb0e279992679599"}, + {file = "threadloop-1.0.2.tar.gz", hash = "sha256:8b180aac31013de13c2ad5c834819771992d350267bddb854613ae77ef571944"}, +] + +[package.dependencies] +tornado = "*" + +[[package]] +name = "thrift" +version = "0.16.0" +description = "Python bindings for the Apache Thrift RPC system" +optional = false +python-versions = "*" +files = [ + {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, +] + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + +[[package]] +name = "tomlkit" +version = "0.12.5" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[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.12" +content-hash = "fbf5a52a364793793ad0d5944f9f222cc7fcc3af331f493284727ff4aa2cd5de" diff --git a/pay-api/pre-hook-update-db.sh b/pay-api/pre-hook-update-db.sh deleted file mode 100755 index 5624bf62f..000000000 --- a/pay-api/pre-hook-update-db.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh -cd /opt/app-root -echo 'starting upgrade' -python3 manage.py db upgrade diff --git a/pay-api/pre_hook_create_database.py b/pay-api/pre_hook_create_database.py deleted file mode 100644 index 11eeef702..000000000 --- a/pay-api/pre_hook_create_database.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright © 2024 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Function to create or upgrade database before application deployment.""" -import contextlib -import os -import sys - -import sqlalchemy -import sqlalchemy.exc -from config import ProdConfig - - -DB_ADMIN_PASSWORD = os.getenv('DB_ADMIN_PASSWORD', None) - -if not hasattr(ProdConfig, 'DB_NAME') or not DB_ADMIN_PASSWORD: - print('Unable to create database.', sys.stdout) - sys.exit(-1) - -DATABASE_URI = 'postgresql://postgres:{password}@{host}:{port}/{name}'.format( - password=DB_ADMIN_PASSWORD, - host=ProdConfig.DB_HOST, - port=int(ProdConfig.DB_PORT), - name='postgres', -) - -with contextlib.suppress(sqlalchemy.exc.ProgrammingError): - with sqlalchemy.create_engine(DATABASE_URI, isolation_level='AUTOCOMMIT').connect() as connection: - DATABASE_NAME = ProdConfig.DB_NAME - connection.execute(f'CREATE DATABASE {DATABASE_NAME}') diff --git a/pay-api/pyproject.toml b/pay-api/pyproject.toml new file mode 100644 index 000000000..b8335b7a8 --- /dev/null +++ b/pay-api/pyproject.toml @@ -0,0 +1,107 @@ +[tool.poetry] +name = "pay-api" +version = "0.1.0" +description = "" +authors = ["BC Registries and Online Services"] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +flask-caching = "2.3.0" +flask-cors = "4.0.0" +flask-migrate = "4.0.7" +flask-moment = "1.0.5" +flask-sqlalchemy = "3.1.1" +flask-script = "2.0.6" +flask = "3.0.2" +jinja2 = "3.1.3" +mako = "1.3.2" +markupsafe = "2.1.5" +sqlalchemy-utils = "0.41.1" +sqlalchemy = "2.0.28" +werkzeug = "3.0.1" +alembic = "1.13.1" +attrs = "23.2.0" +blinker = "1.7.0" +cachelib = "0.9.0" +cachetools = "5.3.3" +cattrs = "23.2.3" +certifi = "2024.2.2" +cffi = "1.16.0" +charset-normalizer = "3.3.2" +click = "8.1.7" +croniter = "2.0.2" +cryptography = "42.0.5" +dpath = "2.1.6" +ecdsa = "0.18.0" +expiringdict = "1.2.2" +flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} +flask-marshmallow = "1.2.0" +gcp-queue = { git = "https://github.com/seeker25/sbc-connect-common.git", subdirectory = "python/gcp-queue", branch = "main" } +greenlet = "3.0.3" +gunicorn = "21.2.0" +holidays = "0.37" +idna = "3.6" +itsdangerous = "2.1.2" +jaeger-client = "4.8.0" +jsonschema = "4.17.3" +launchdarkly-eventsource = "1.1.1" +launchdarkly-server-sdk = "8.2.1" +marshmallow-sqlalchemy = "1.0.0" +marshmallow = "3.21.1" +opentracing = "2.4.0" +packaging = "24.0" +proto-plus = "1.23.0" +protobuf = "4.25.3" +psycopg2-binary = "2.9.9" +pyrfc3339 = "1.1" +pyasn1-modules = "0.3.0" +pyasn1 = "0.5.1" +pycparser = "2.21" +pyhumps = "3.8.0" +pyrsistent = "0.20.0" +python-dateutil = "2.9.0.post0" +python-dotenv = "1.0.1" +python-jose = "3.3.0" +pytz = "2024.1" +requests = "2.31.0" +rsa = "4.9" +semver = "3.0.2" +sentry-sdk = "1.41.0" +six = "1.16.0" +threadloop = "1.0.2" +thrift = "0.16.0" +tornado = "6.4" +typing-extensions = "4.10.0" +urllib3 = "2.2.1" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +pg8000 = "^1.30.5" +sql-versioning = { git = "https://github.com/bcgov/lear.git", subdirectory = "python/common/sql-versioning", branch = "feature-legal-name" } + + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pytest-mock = "^3.12.0" +requests = "^2.31.0" +pyhamcrest = "^2.1.0" +pytest-cov = "^4.1.0" +freezegun = "^1.4.0" +flake8 = "^7.0.0" +flake8-blind-except = "^0.2.1" +flake8-debugger = "^4.1.2" +flake8-docstrings = "^1.7.0" +flake8-isort = "^6.1.1" +flake8-quotes = "^3.4.0" +pep8-naming = "^0.13.3" +autopep8 = "^2.0.4" +coverage = "^7.4.3" +pylint = "^3.1.0" +pylint-flask = "^0.6" +pydocstyle = "^6.3.0" +isort = "^5.13.2" +lovely-pytest-docker = "^0.3.1" +faker = "^24.2.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/pay-api/requirements.txt b/pay-api/requirements.txt deleted file mode 100644 index 7d5f60c7d..000000000 --- a/pay-api/requirements.txt +++ /dev/null @@ -1,69 +0,0 @@ -Flask-Caching==2.3.0 -Flask-Cors==3.0.10 -Flask-Migrate==2.7.0 -Flask-Moment==1.0.5 -Flask-SQLAlchemy==2.5.1 -Flask-Script==2.0.6 -Flask==1.1.2 -Jinja2==3.0.3 -Mako==1.3.5 -MarkupSafe==2.1.5 -SQLAlchemy-Continuum==1.3.15 -SQLAlchemy-Utils==0.41.2 -SQLAlchemy==1.3.24 -Werkzeug==1.0.1 -alembic==1.13.1 -attrs==23.2.0 -blinker==1.8.2 -cachelib==0.9.0 -cattrs==23.2.3 -certifi==2024.2.2 -cffi==1.16.0 -charset-normalizer==3.3.2 -click==8.1.7 -croniter==2.0.5 -cryptography==42.0.7 -dpath==2.1.6 -ecdsa==0.19.0 -exceptiongroup==1.2.1 -expiringdict==1.2.2 -flask-jwt-oidc==0.3.0 -flask-marshmallow==0.11.0 -gunicorn==22.0.0 -holidays==0.37 -idna==3.7 -importlib_metadata==7.1.0 -importlib_resources==6.4.0 -itsdangerous==2.0.1 -jaeger-client==4.8.0 -jsonschema==4.17.3 -launchdarkly-server-sdk==8.2.1 -marshmallow-sqlalchemy==0.25.0 -marshmallow==3.21.2 -opentracing==2.4.0 -packaging==24.0 -pkgutil_resolve_name==1.3.10 -protobuf==3.19.6 -psycopg2-binary==2.9.9 -pyRFC3339==1.1 -pyasn1==0.6.0 -pycparser==2.22 -pyhumps==3.8.0 -pyrsistent==0.20.0 -python-dateutil==2.9.0.post0 -python-dotenv==1.0.1 -python-jose==3.3.0 -pytz==2024.1 -requests==2.32.2 -rsa==4.9 -semver==3.0.2 -sentry-sdk==2.3.1 -six==1.16.0 -threadloop==1.0.2 -thrift==0.20.0 -tornado==6.4 -typing_extensions==4.12.0 -urllib3==2.2.1 -zipp==3.19.0 --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/seeker25/sbc-connect-common.git@small_tweaks#egg=gcp-queue&subdirectory=python/gcp-queue diff --git a/pay-api/requirements/dev.txt b/pay-api/requirements/dev.txt deleted file mode 100755 index ad98209c9..000000000 --- a/pay-api/requirements/dev.txt +++ /dev/null @@ -1,31 +0,0 @@ -# Everything the developer needs in addition to the production requirements --r prod.txt - -# Testing -pytest -pytest-mock -requests -pyhamcrest -pytest-cov -FreezeGun - -# Lint and code style -flake8==5.0.4 -flake8-blind-except -flake8-debugger -flake8-docstrings -flake8-isort -flake8-quotes -pep8-naming -autopep8 -coverage -pylint -pylint-flask -pydocstyle -isort - -# docker -lovely-pytest-docker -pytest-asyncio==0.18.3 - -Faker diff --git a/pay-api/requirements/prod.txt b/pay-api/requirements/prod.txt deleted file mode 100644 index fc4030889..000000000 --- a/pay-api/requirements/prod.txt +++ /dev/null @@ -1,31 +0,0 @@ -gunicorn -Flask -Flask-Caching -Flask-Cors==3.0.10 -Flask-Migrate<3 -Flask-Script -Flask-Moment -Flask-SQLAlchemy -flask-marshmallow==0.11.0 -flask-jwt-oidc -python-dotenv -psycopg2-binary -marshmallow-sqlalchemy==0.25.0 -jsonschema==4.17.3 -requests -croniter -sentry-sdk[flask] -cattrs -jaeger-client -dpath -Werkzeug<2 -SQLAlchemy-Continuum -cryptography -sqlalchemy_utils -sqlalchemy<1.4 -itsdangerous==2.0.1 -Jinja2==3.0.3 -protobuf~=3.19.5 -launchdarkly-server-sdk==8.2.1 -holidays==0.37 -pyhumps diff --git a/pay-api/requirements/repo-libraries.txt b/pay-api/requirements/repo-libraries.txt deleted file mode 100644 index 01eec33a2..000000000 --- a/pay-api/requirements/repo-libraries.txt +++ /dev/null @@ -1,2 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/seeker25/sbc-connect-common.git@small_tweaks#egg=gcp-queue&subdirectory=python/gcp-queue diff --git a/pay-api/setup.cfg b/pay-api/setup.cfg index a0244863c..ccbd8d7ce 100755 --- a/pay-api/setup.cfg +++ b/pay-api/setup.cfg @@ -9,7 +9,7 @@ classifiers = Topic :: Payment License :: OSI Approved :: Apache Software License Natural Language :: English - Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.12 license = Apache Software License Version 2.0 description = A short description of the project long_description = file: README.md @@ -17,7 +17,7 @@ keywords = [options] zip_safe = True -python_requires = >=3.8.5 +python_requires = >=3.12 include_package_data = True packages = find: @@ -45,7 +45,7 @@ per-file-ignores = max_line_length = 120 ignore = E501 docstring-min-length=10 -notes=FIXME,XXX # TODO is ignored +notes=FIXME,XXX match_dir = src/pay_api ignored-modules=flask_sqlalchemy sqlalchemy diff --git a/pay-api/src/pay_api/__init__.py b/pay-api/src/pay_api/__init__.py index 23240d090..c5216cd3e 100755 --- a/pay-api/src/pay_api/__init__.py +++ b/pay-api/src/pay_api/__init__.py @@ -18,9 +18,10 @@ import os +from flask_migrate import Migrate, upgrade import sentry_sdk # noqa: I001; pylint: disable=ungrouped-imports,wrong-import-order; conflicts with Flake8 from flask import Flask -from sbc_common_components.exception_handling.exception_handler import ExceptionHandler # noqa: I001 +from sbc_common_components.exception_handling.exception_handler import ExceptionHandler from sbc_common_components.utils.camel_case_response import convert_to_camel from sentry_sdk.integrations.flask import FlaskIntegration # noqa: I001 @@ -30,17 +31,17 @@ from pay_api.resources import endpoints from pay_api.services.flags import flags from pay_api.models import db, ma +from pay_api.services.gcp_queue import queue from pay_api.utils.auth import jwt from pay_api.utils.cache import cache from pay_api.utils.logging import setup_logging from pay_api.utils.run_version import get_run_version -from .services.gcp_queue import queue setup_logging(os.path.join(_Config.PROJECT_ROOT, 'logging.conf')) -def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): +def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')): """Return a configured Flask App using the Factory method.""" app = Flask(__name__) app.env = run_mode @@ -49,43 +50,49 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): flags.init_app(app) queue.init_app(app) db.init_app(app) + queue.init_app(app) + Migrate(app, db) + app.logger.info('Running migration upgrade.') + with app.app_context(): + upgrade(directory='migrations', revision='head', sql=False, tag=None) + # Alembic has it's own logging config, we'll need to restore our logging here. + setup_logging(os.path.join(_Config.PROJECT_ROOT, 'logging.conf')) + app.logger.info('Finished migration upgrade.') ma.init_app(app) endpoints.init_app(app) - if run_mode != 'migration': - - # Configure Sentry - if str(app.config.get('SENTRY_ENABLE')).lower() == 'true': - if app.config.get('SENTRY_DSN', None): # pragma: no cover - sentry_sdk.init( # pylint: disable=abstract-class-instantiated - dsn=app.config.get('SENTRY_DSN'), - integrations=[FlaskIntegration()] - ) + # Configure Sentry + if str(app.config.get('SENTRY_ENABLE')).lower() == 'true': + if app.config.get('SENTRY_DSN', None): # pragma: no cover + sentry_sdk.init( # pylint: disable=abstract-class-instantiated + dsn=app.config.get('SENTRY_DSN'), + integrations=[FlaskIntegration()] + ) - app.after_request(convert_to_camel) + app.after_request(convert_to_camel) - setup_jwt_manager(app, jwt) + setup_jwt_manager(app, jwt) - ExceptionHandler(app) + ExceptionHandler(app) - @app.after_request - def handle_after_request(response): # pylint: disable=unused-variable - add_version(response) - set_access_control_header(response) - return response + @app.after_request + def handle_after_request(response): # pylint: disable=unused-variable + add_version(response) + set_access_control_header(response) + return response - def set_access_control_header(response): - response.headers['Access-Control-Allow-Origin'] = '*' - response.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type, registries-trace-id, ' \ - 'Account-Id' + def set_access_control_header(response): + response.headers['Access-Control-Allow-Origin'] = '*' + response.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type, registries-trace-id, ' \ + 'Account-Id' - def add_version(response): # pylint: disable=unused-variable - version = get_run_version() - response.headers['API'] = f'pay_api/{version}' - return response + def add_version(response): # pylint: disable=unused-variable + version = get_run_version() + response.headers['API'] = f'pay_api/{version}' + return response - register_shellcontext(app) - build_cache(app) + register_shellcontext(app) + build_cache(app) return app diff --git a/pay-api/src/pay_api/config.py b/pay-api/src/pay_api/config.py index 06b9821db..5d2593124 100755 --- a/pay-api/src/pay_api/config.py +++ b/pay-api/src/pay_api/config.py @@ -1,13 +1,13 @@ # Copyright © 2024 Province of British Columbia # -# Licensed under the Apache License, Version 2.0 (the "License"); +# Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, +# distributed under the License is distributed on an 'AS IS' BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. @@ -51,7 +51,7 @@ def get_named_config(config_name: str = 'production'): elif config_name == 'migration': config = MigrationConfig() else: - raise KeyError(f"Unknown configuration '{config_name}'") + raise KeyError(f'Unknown configuration "{config_name}"') return config @@ -61,11 +61,10 @@ def _get_config(config_key: str, **kwargs): value = os.getenv(config_key, kwargs.get('default')) else: value = os.getenv(config_key) - # assert value TODO Un-comment once we find a solution to run pre-hook without initializing app return value -class _Config(): # pylint: disable=too-few-public-methods +class _Config: # pylint: disable=too-few-public-methods """Base class configuration that should set reasonable defaults for all the other configurations.""" PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__)) @@ -84,18 +83,26 @@ class _Config(): # pylint: disable=too-few-public-methods DB_NAME = _get_config('DATABASE_NAME') DB_HOST = _get_config('DATABASE_HOST') DB_PORT = _get_config('DATABASE_PORT', default='5432') - SQLALCHEMY_DATABASE_URI = f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' SQLALCHEMY_ECHO = _get_config('SQLALCHEMY_ECHO', default='False').lower() == 'true' + # POSTGRESQL + if DB_UNIX_SOCKET := os.getenv('DATABASE_UNIX_SOCKET', None): + SQLALCHEMY_DATABASE_URI = ( + f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432' + ) + else: + SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}' + # JWT_OIDC Settings JWT_OIDC_WELL_KNOWN_CONFIG = _get_config('JWT_OIDC_WELL_KNOWN_CONFIG') JWT_OIDC_ALGORITHMS = _get_config('JWT_OIDC_ALGORITHMS') - JWT_OIDC_JWKS_URI = _get_config('JWT_OIDC_JWKS_URI', default=None) JWT_OIDC_ISSUER = _get_config('JWT_OIDC_ISSUER') JWT_OIDC_AUDIENCE = _get_config('JWT_OIDC_AUDIENCE') JWT_OIDC_CLIENT_SECRET = _get_config('JWT_OIDC_CLIENT_SECRET') JWT_OIDC_CACHING_ENABLED = _get_config('JWT_OIDC_CACHING_ENABLED', default=False) - JWT_OIDC_JWKS_CACHE_TIMEOUT = int(_get_config('JWT_OIDC_JWKS_CACHE_TIMEOUT', default=300)) + JWT_OIDC_JWKS_CACHE_TIMEOUT = int( + _get_config('JWT_OIDC_JWKS_CACHE_TIMEOUT', default=300) + ) # CFS API Settings CFS_BASE_URL = _get_config('CFS_BASE_URL') @@ -103,7 +110,9 @@ class _Config(): # pylint: disable=too-few-public-methods CFS_CLIENT_SECRET = _get_config('CFS_CLIENT_SECRET') PAYBC_PORTAL_URL = _get_config('PAYBC_PORTAL_URL') CONNECT_TIMEOUT = int(_get_config('CONNECT_TIMEOUT', default=10)) - GENERATE_RANDOM_INVOICE_NUMBER = _get_config('CFS_GENERATE_RANDOM_INVOICE_NUMBER', default='False') + GENERATE_RANDOM_INVOICE_NUMBER = _get_config( + 'CFS_GENERATE_RANDOM_INVOICE_NUMBER', default='False' + ) CFS_ACCOUNT_DESCRIPTION = _get_config('CFS_ACCOUNT_DESCRIPTION', default='BCR') CFS_INVOICE_PREFIX = os.getenv('CFS_INVOICE_PREFIX', 'REG') CFS_RECEIPT_PREFIX = os.getenv('CFS_RECEIPT_PREFIX', 'RCPT') @@ -119,7 +128,9 @@ class _Config(): # pylint: disable=too-few-public-methods PAYBC_DIRECT_PAY_BASE_URL = _get_config('PAYBC_DIRECT_PAY_BASE_URL') PAYBC_DIRECT_PAY_CLIENT_ID = _get_config('PAYBC_DIRECT_PAY_CLIENT_ID') PAYBC_DIRECT_PAY_CLIENT_SECRET = _get_config('PAYBC_DIRECT_PAY_CLIENT_SECRET') - PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL = _get_config('PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL') + PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL = _get_config( + 'PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL' + ) # PUB/SUB - PUB: auth-event-dev, account-mailer-dev, business-pay-dev, namex-pay-dev ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', 'account-mailer-dev') @@ -128,57 +139,75 @@ class _Config(): # pylint: disable=too-few-public-methods GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) NAMEX_PAY_TOPIC = os.getenv('NAMEX_PAY_TOPIC', 'namex-pay-dev') - # Auth API Endpoint - AUTH_API_ENDPOINT = f'{_get_config("AUTH_API_URL")}/' + # API Endpoints + AUTH_API_URL = os.getenv('AUTH_API_URL', '') + AUTH_API_VERSION = os.getenv('AUTH_API_VERSION', '') + BCOL_API_URL = os.getenv('BCOL_API_URL', '') + BCOL_API_VERSION = os.getenv('BCOL_API_VERSION', '') + REPORT_API_URL = os.getenv('REPORT_API_URL', '') + REPORT_API_VERSION = os.getenv('REPORT_API_VERSION', '') - # REPORT API Settings - REPORT_API_BASE_URL = f'{_get_config("REPORT_API_URL")}/reports' - - # BCOL Service - BCOL_API_ENDPOINT = _get_config('BCOL_API_URL') + AUTH_API_ENDPOINT = f'{AUTH_API_URL + AUTH_API_VERSION}/' + REPORT_API_BASE_URL = f'{REPORT_API_URL + REPORT_API_VERSION}/reports' + BCOL_API_ENDPOINT = f'{BCOL_API_URL + BCOL_API_VERSION}/' # Sentry Config SENTRY_ENABLE = _get_config('SENTRY_ENABLE', default=False) SENTRY_DSN = _get_config('SENTRY_DSN', default=None) # Disable valid redirect URLs - for DEV only - DISABLE_VALID_REDIRECT_URLS = _get_config('DISABLE_VALID_REDIRECT_URLS', default='False').lower() == 'true' + DISABLE_VALID_REDIRECT_URLS = ( + _get_config('DISABLE_VALID_REDIRECT_URLS', default='False').lower() == 'true' + ) # Valid Payment redirect URLs - VALID_REDIRECT_URLS = [(val.strip() if val != '' else None) - for val in _get_config('VALID_REDIRECT_URLS', default='').split(',')] + VALID_REDIRECT_URLS = [ + (val.strip() if val != '' else None) + for val in _get_config('VALID_REDIRECT_URLS', default='').split(',') + ] # Service account details KEYCLOAK_SERVICE_ACCOUNT_ID = _get_config('SBC_AUTH_ADMIN_CLIENT_ID') KEYCLOAK_SERVICE_ACCOUNT_SECRET = _get_config('SBC_AUTH_ADMIN_CLIENT_SECRET') # Default number of transactions to be returned for transaction reporting - TRANSACTION_REPORT_DEFAULT_TOTAL = int(_get_config('TRANSACTION_REPORT_DEFAULT_TOTAL', default=50)) + TRANSACTION_REPORT_DEFAULT_TOTAL = int( + _get_config('TRANSACTION_REPORT_DEFAULT_TOTAL', default=50) + ) # Default number of routing slips to be returned for routing slip search - ROUTING_SLIP_DEFAULT_TOTAL = int(_get_config('ROUTING_SLIP_DEFAULT_TOTAL', default=50)) + ROUTING_SLIP_DEFAULT_TOTAL = int( + _get_config('ROUTING_SLIP_DEFAULT_TOTAL', default=50) + ) - PAD_CONFIRMATION_PERIOD_IN_DAYS = int(_get_config('PAD_CONFIRMATION_PERIOD_IN_DAYS', default=3)) + PAD_CONFIRMATION_PERIOD_IN_DAYS = int( + _get_config('PAD_CONFIRMATION_PERIOD_IN_DAYS', default=3) + ) # legislative timezone for future effective dating LEGISLATIVE_TIMEZONE = os.getenv('LEGISLATIVE_TIMEZONE', 'America/Vancouver') # BCOL user name for Service account payments - BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS = os.getenv('BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS', - 'BCROS SERVICE ACCOUNT') + BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS = os.getenv( + 'BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS', 'BCROS SERVICE ACCOUNT' + ) # The number of characters which can be exposed to admins for a bank account number MASK_LEN = int(_get_config('MASK_LEN', default=3)) # Config value to disable activity logs - DISABLE_ACTIVITY_LOGS = os.getenv('DISABLE_ACTIVITY_LOGS', 'False').lower() == 'true' + DISABLE_ACTIVITY_LOGS = ( + os.getenv('DISABLE_ACTIVITY_LOGS', 'False').lower() == 'true' + ) # Secret key for encrypting bank account ACCOUNT_SECRET_KEY = os.getenv('ACCOUNT_SECRET_KEY') OUTSTANDING_TRANSACTION_DAYS = int(os.getenv('OUTSTANDING_TRANSACTION_DAYS', '10')) - ALLOW_LEGACY_ROUTING_SLIPS = os.getenv('ALLOW_LEGACY_ROUTING_SLIPS', 'True').lower() == 'true' + ALLOW_LEGACY_ROUTING_SLIPS = ( + os.getenv('ALLOW_LEGACY_ROUTING_SLIPS', 'True').lower() == 'true' + ) TESTING = False DEBUG = True @@ -208,7 +237,7 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods DB_PORT = _get_config('DATABASE_TEST_PORT', default='5432') SQLALCHEMY_DATABASE_URI = _get_config( 'DATABASE_TEST_URL', - default=f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' + default=f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}', ) JWT_OIDC_TEST_MODE = True @@ -228,8 +257,8 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods 'alg': 'RS256', 'use': 'sig', 'n': 'AN-fWcpCyE5KPzHDjigLaSUVZI0uYrcGcc40InVtl-rQRDmAh-C2W8H4_Hxhr5VLc6crsJ2LiJTV_E72S03pzpOOaaYV6-' - 'TzAjCou2GYJIXev7f6Hh512PuG5wyxda_TlBSsI-gvphRTPsKCnPutrbiukCYrnPuWxX5_cES9eStR', - 'e': 'AQAB' + 'TzAjCou2GYJIXev7f6Hh512PuG5wyxda_TlBSsI-gvphRTPsKCnPutrbiukCYrnPuWxX5_cES9eStR', + 'e': 'AQAB', } ] } @@ -242,16 +271,16 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods 'alg': 'RS256', 'use': 'sig', 'n': 'AN-fWcpCyE5KPzHDjigLaSUVZI0uYrcGcc40InVtl-rQRDmAh-C2W8H4_Hxhr5VLc6crsJ2LiJTV_E72S03pzpOOaaYV6-' - 'TzAjCou2GYJIXev7f6Hh512PuG5wyxda_TlBSsI-gvphRTPsKCnPutrbiukCYrnPuWxX5_cES9eStR', + 'TzAjCou2GYJIXev7f6Hh512PuG5wyxda_TlBSsI-gvphRTPsKCnPutrbiukCYrnPuWxX5_cES9eStR', 'e': 'AQAB', 'd': 'C0G3QGI6OQ6tvbCNYGCqq043YI_8MiBl7C5dqbGZmx1ewdJBhMNJPStuckhskURaDwk4-' - '8VBW9SlvcfSJJrnZhgFMjOYSSsBtPGBIMIdM5eSKbenCCjO8Tg0BUh_' - 'xa3CHST1W4RQ5rFXadZ9AeNtaGcWj2acmXNO3DVETXAX3x0', + '8VBW9SlvcfSJJrnZhgFMjOYSSsBtPGBIMIdM5eSKbenCCjO8Tg0BUh_' + 'xa3CHST1W4RQ5rFXadZ9AeNtaGcWj2acmXNO3DVETXAX3x0', 'p': 'APXcusFMQNHjh6KVD_hOUIw87lvK13WkDEeeuqAydai9Ig9JKEAAfV94W6Aftka7tGgE7ulg1vo3eJoLWJ1zvKM', 'q': 'AOjX3OnPJnk0ZFUQBwhduCweRi37I6DAdLTnhDvcPTrrNWuKPg9uGwHjzFCJgKd8KBaDQ0X1rZTZLTqi3peT43s', 'dp': 'AN9kBoA5o6_Rl9zeqdsIdWFmv4DB5lEqlEnC7HlAP-3oo3jWFO9KQqArQL1V8w2D4aCd0uJULiC9pCP7aTHvBhc', 'dq': 'ANtbSY6njfpPploQsF9sU26U0s7MsuLljM1E8uml8bVJE1mNsiu9MgpUvg39jEu9BtM2tDD7Y51AAIEmIQex1nM', - 'qi': 'XLE5O360x-MhsdFXx8Vwz4304-MJg-oGSJXCK_ZWYOB_FGXFRTfebxCsSYi0YwJo-oNu96bvZCuMplzRI1liZw' + 'qi': 'XLE5O360x-MhsdFXx8Vwz4304-MJg-oGSJXCK_ZWYOB_FGXFRTfebxCsSYi0YwJo-oNu96bvZCuMplzRI1liZw', } ] } @@ -315,7 +344,7 @@ class ProdConfig(_Config): # pylint: disable=too-few-public-methods DEBUG = False -class MigrationConfig(): # pylint: disable=too-few-public-methods +class MigrationConfig: # pylint: disable=too-few-public-methods """Config for db migration.""" TESTING = False @@ -327,5 +356,7 @@ class MigrationConfig(): # pylint: disable=too-few-public-methods DB_NAME = _get_config('DATABASE_NAME') DB_HOST = _get_config('DATABASE_HOST') DB_PORT = _get_config('DATABASE_PORT', default='5432') - SQLALCHEMY_DATABASE_URI = f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' + SQLALCHEMY_DATABASE_URI = ( + f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' + ) SQLALCHEMY_TRACK_MODIFICATIONS = False diff --git a/pay-api/src/pay_api/factory/payment_system_factory.py b/pay-api/src/pay_api/factory/payment_system_factory.py index 33f9fc56d..b9000b55f 100644 --- a/pay-api/src/pay_api/factory/payment_system_factory.py +++ b/pay-api/src/pay_api/factory/payment_system_factory.py @@ -109,7 +109,8 @@ def create(**kwargs): @staticmethod def _validate_and_throw_error(instance: PaymentSystemService, payment_account: PaymentAccount): if isinstance(instance, PadService): - is_in_pad_confirmation_period = payment_account.pad_activation_date > datetime.now() + is_in_pad_confirmation_period = payment_account.pad_activation_date > \ + datetime.now(payment_account.pad_activation_date.tzinfo) is_cfs_account_in_pending_status = payment_account.cfs_account_status == \ CfsAccountStatus.PENDING_PAD_ACTIVATION.value diff --git a/pay-api/src/pay_api/logging.conf b/pay-api/src/pay_api/logging.conf index 3d8bb0e2d..ec7f7f03f 100755 --- a/pay-api/src/pay_api/logging.conf +++ b/pay-api/src/pay_api/logging.conf @@ -1,5 +1,5 @@ [loggers] -keys=root,api,tracing +keys=root,api [handlers] keys=console @@ -17,12 +17,6 @@ handlers=console qualname=pay_api propagate=0 -[logger_tracing] -level=ERROR -handlers=console -qualname=jaeger_tracing -propagate=0 - [handler_console] class=StreamHandler level=DEBUG diff --git a/pay-api/src/pay_api/models/__init__.py b/pay-api/src/pay_api/models/__init__.py index ffcd4e2f5..c8f85920e 100755 --- a/pay-api/src/pay_api/models/__init__.py +++ b/pay-api/src/pay_api/models/__init__.py @@ -32,11 +32,12 @@ from .eft_file import EFTFile from .eft_gl_transfers import EFTGLTransfer from .eft_process_status_code import EFTProcessStatusCode -from .eft_short_names import EFTShortnames, EFTShortnameSchema +from .eft_short_names import EFTShortnames, EFTShortnameSchema, EFTShortnameSummarySchema +from .eft_short_name_links import EFTShortnameLinks, EFTShortnameLinkSchema from .eft_transaction import EFTTransaction, EFTTransactionSchema from .ejv_file import EjvFile from .ejv_header import EjvHeader -from .ejv_invoice_link import EjvInvoiceLink +from .ejv_link import EjvLink from .error_code import ErrorCode, ErrorCodeSchema from .fee_code import FeeCode, FeeCodeSchema # noqa: I001 from .fee_schedule import FeeSchedule, FeeScheduleSchema diff --git a/pay-api/src/pay_api/models/account_fee.py b/pay-api/src/pay_api/models/account_fee.py index f883529b3..a23a340ef 100644 --- a/pay-api/src/pay_api/models/account_fee.py +++ b/pay-api/src/pay_api/models/account_fee.py @@ -17,9 +17,10 @@ from marshmallow import fields, post_dump from sqlalchemy import Boolean, ForeignKey from sqlalchemy.orm import relationship +from sql_versioning import Versioned from .audit import Audit -from .base_model import VersionedModel +from .base_model import BaseModel from .base_schema import BaseSchema from .corp_type import CorpType from .db import db @@ -27,7 +28,7 @@ from .payment_account import PaymentAccount -class AccountFee(Audit, VersionedModel): +class AccountFee(Audit, Versioned, BaseModel): """This class manages all of the base data about Account Fees.""" __tablename__ = 'account_fees' diff --git a/pay-api/src/pay_api/models/base_model.py b/pay-api/src/pay_api/models/base_model.py index 9bc053939..7ad36bbac 100644 --- a/pay-api/src/pay_api/models/base_model.py +++ b/pay-api/src/pay_api/models/base_model.py @@ -13,12 +13,8 @@ # limitations under the License. """Super class to handle all operations related to base model.""" -from flask import current_app -from sqlalchemy_continuum.plugins.flask import fetch_remote_addr -from pay_api.utils.user_context import user_context - -from .db import activity_plugin, db +from .db import db class BaseModel(db.Model): @@ -35,7 +31,6 @@ def flush(self): """Save and flush.""" db.session.add(self) db.session.flush() - self.create_activity(self) return self def save_or_add(self, auto_save: bool): @@ -49,16 +44,12 @@ def save_or_add(self, auto_save: bool): def save(self): """Save and commit.""" db.session.add(self) - db.session.flush() - self.create_activity(self) db.session.commit() return self def delete(self): """Delete and commit.""" db.session.delete(self) - db.session.flush() - self.create_activity(self, is_delete=True) db.session.commit() @staticmethod @@ -69,36 +60,4 @@ def rollback(): @classmethod def find_by_id(cls, identifier: int): """Return model by id.""" - return cls.query.get(identifier) - - @classmethod - def create_activity(cls, obj, is_delete=False): - """Create activity records if the model is versioned.""" - if isinstance(obj, VersionedModel) and not current_app.config.get('DISABLE_ACTIVITY_LOGS'): - if is_delete: - verb = 'delete' - else: - verb = 'update' - - activity = activity_plugin.activity_cls(verb=verb, object=obj, data={ - 'user_name': cls._get_user_name(), - 'remote_addr': fetch_remote_addr() - }) - - db.session.add(activity) - - @staticmethod - @user_context - def _get_user_name(**kwargs): - """Return current user user_name.""" - return kwargs['user'].user_name - - -class VersionedModel(BaseModel): - """This class manages all of the base code, type or status model functions.""" - - __abstract__ = True - - __versioned__ = { - 'exclude': [] - } + return db.session.get(cls, identifier) diff --git a/pay-api/src/pay_api/models/base_schema.py b/pay-api/src/pay_api/models/base_schema.py index a2fdf00b6..d628f1cb6 100644 --- a/pay-api/src/pay_api/models/base_schema.py +++ b/pay-api/src/pay_api/models/base_schema.py @@ -18,7 +18,7 @@ from .db import ma -class BaseSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class BaseSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Base Schema.""" def __init__(self, *args, **kwargs): @@ -31,6 +31,7 @@ class Meta: # pylint: disable=too-few-public-methods """Meta class to declare any class attributes.""" datetimeformat = '%Y-%m-%dT%H:%M:%S+00:00' + load_instance = True @post_dump(pass_many=True) def _remove_empty(self, data, many): diff --git a/pay-api/src/pay_api/models/cfs_account.py b/pay-api/src/pay_api/models/cfs_account.py index a25d21e60..2d13c78ac 100644 --- a/pay-api/src/pay_api/models/cfs_account.py +++ b/pay-api/src/pay_api/models/cfs_account.py @@ -15,6 +15,7 @@ from __future__ import annotations from typing import List from flask import current_app +from sql_versioning import Versioned from sqlalchemy import ForeignKey from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.orm import relationship @@ -24,11 +25,11 @@ from pay_api.utils.enums import CfsAccountStatus from .base_schema import BaseSchema -from .base_model import VersionedModel +from .base_model import BaseModel from .db import db -class CfsAccount(VersionedModel): # pylint:disable=too-many-instance-attributes +class CfsAccount(Versioned, BaseModel): # pylint:disable=too-many-instance-attributes """This class manages all of the base data about PayBC Account.""" __tablename__ = 'cfs_accounts' diff --git a/pay-api/src/pay_api/models/cfs_account_status_code.py b/pay-api/src/pay_api/models/cfs_account_status_code.py index f17b26f0b..b13ba1a3c 100644 --- a/pay-api/src/pay_api/models/cfs_account_status_code.py +++ b/pay-api/src/pay_api/models/cfs_account_status_code.py @@ -42,10 +42,11 @@ class CfsAccountStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class CfsAccountStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class CfsAccountStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = CfsAccountStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/corp_type.py b/pay-api/src/pay_api/models/corp_type.py index dcdd69e5c..587e59cc5 100644 --- a/pay-api/src/pay_api/models/corp_type.py +++ b/pay-api/src/pay_api/models/corp_type.py @@ -70,10 +70,11 @@ def __str__(self): return f'{self.code}' -class CorpTypeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class CorpTypeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Business.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = CorpType + load_instance = True diff --git a/pay-api/src/pay_api/models/credit.py b/pay-api/src/pay_api/models/credit.py index 177d8863a..d604f1e3c 100644 --- a/pay-api/src/pay_api/models/credit.py +++ b/pay-api/src/pay_api/models/credit.py @@ -63,10 +63,11 @@ def find_by_cfs_identifier(cls, cfs_identifier: str, credit_memo: bool = False): return cls.query.filter_by(cfs_identifier=cfs_identifier).filter_by(is_credit_memo=credit_memo).one_or_none() -class CreditSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class CreditSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Credit.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = Credit + load_instance = True diff --git a/pay-api/src/pay_api/models/custom_query.py b/pay-api/src/pay_api/models/custom_query.py index 553fbab68..7709db44f 100644 --- a/pay-api/src/pay_api/models/custom_query.py +++ b/pay-api/src/pay_api/models/custom_query.py @@ -11,13 +11,14 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# pylint: disable=W0223 """Custom Query class to extend BaseQuery class functionality.""" from datetime import date, datetime -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query from sqlalchemy import and_, func -class CustomQuery(BaseQuery): +class CustomQuery(Query): # pylint: disable=too-many-ancestors """Custom Query class to extend the base query class for helper functionality.""" def filter_conditionally(self, search_criteria, model_attribute, is_like: bool = False): diff --git a/pay-api/src/pay_api/models/db.py b/pay-api/src/pay_api/models/db.py index bfd1f6b32..9ee95c8fd 100755 --- a/pay-api/src/pay_api/models/db.py +++ b/pay-api/src/pay_api/models/db.py @@ -17,16 +17,11 @@ """ from flask_marshmallow import Marshmallow from flask_sqlalchemy import SQLAlchemy -from sqlalchemy_continuum import make_versioned -from sqlalchemy_continuum.plugins import ActivityPlugin +from sql_versioning import versioned_session from .custom_query import CustomQuery # by convention in the Flask community these are lower case, # whereas pylint wants them upper case ma = Marshmallow() # pylint: disable=invalid-name db = SQLAlchemy(query_class=CustomQuery) # pylint: disable=invalid-name - - -activity_plugin = ActivityPlugin() # pylint: disable=invalid-name - -make_versioned(user_cls=None, plugins=[activity_plugin]) +versioned_session(db.session) diff --git a/pay-api/src/pay_api/models/disbursement_status_code.py b/pay-api/src/pay_api/models/disbursement_status_code.py index 5107f47ac..bed1297e1 100644 --- a/pay-api/src/pay_api/models/disbursement_status_code.py +++ b/pay-api/src/pay_api/models/disbursement_status_code.py @@ -42,10 +42,11 @@ class DisbursementStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class DisbursementStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class DisbursementStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = DisbursementStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/distribution_code.py b/pay-api/src/pay_api/models/distribution_code.py index 3e7bea816..f0a386306 100644 --- a/pay-api/src/pay_api/models/distribution_code.py +++ b/pay-api/src/pay_api/models/distribution_code.py @@ -17,11 +17,11 @@ from datetime import date from marshmallow import fields +from sql_versioning import Versioned from sqlalchemy import Boolean, ForeignKey from sqlalchemy.orm import relationship from .audit import Audit, AuditSchema, BaseModel -from .base_model import VersionedModel from .base_schema import BaseSchema from .db import db, ma @@ -72,7 +72,7 @@ def bulk_save_links(cls, links: list): BaseModel.commit() -class DistributionCode(Audit, VersionedModel): # pylint:disable=too-many-instance-attributes +class DistributionCode(Audit, Versioned, BaseModel): # pylint:disable=too-many-instance-attributes """This class manages all of the base data about distribution code. Distribution code holds details on the codes for how the collected payment is going to be distributed. @@ -156,7 +156,7 @@ def find_by_active_for_account(cls, account_id: int): return distribution_code -class DistributionCodeLinkSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class DistributionCodeLinkSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the DistributionCodeLink.""" class Meta: # pylint: disable=too-few-public-methods @@ -164,6 +164,7 @@ class Meta: # pylint: disable=too-few-public-methods model = DistributionCodeLink exclude = ['disbursement'] + load_instance = True class DistributionCodeSchema(AuditSchema, BaseSchema): # pylint: disable=too-many-ancestors diff --git a/pay-api/src/pay_api/models/eft_credit.py b/pay-api/src/pay_api/models/eft_credit.py index e16aaab8c..5a8927d12 100644 --- a/pay-api/src/pay_api/models/eft_credit.py +++ b/pay-api/src/pay_api/models/eft_credit.py @@ -61,11 +61,3 @@ class EFTCredit(BaseModel): # pylint:disable=too-many-instance-attributes def find_by_payment_account_id(cls, payment_account_id: int): """Find EFT Credit by payment account id.""" return cls.query.filter_by(payment_account_id=payment_account_id).all() - - @classmethod - def update_account_by_short_name_id(cls, short_name_id: int, payment_account_id: int): - """Update all payment account ids for short name.""" - db.session.query(EFTCredit) \ - .filter(EFTCredit.short_name_id == short_name_id) \ - .update({EFTCredit.payment_account_id: payment_account_id}, synchronize_session='fetch') - db.session.commit() diff --git a/pay-api/src/pay_api/models/eft_credit_invoice_link.py b/pay-api/src/pay_api/models/eft_credit_invoice_link.py index 9cf6c372d..46e38a463 100644 --- a/pay-api/src/pay_api/models/eft_credit_invoice_link.py +++ b/pay-api/src/pay_api/models/eft_credit_invoice_link.py @@ -37,13 +37,17 @@ class EFTCreditInvoiceLink(BaseModel): # pylint: disable=too-few-public-methods __mapper_args__ = { 'include_properties': [ 'id', + 'amount', 'created_on', 'eft_credit_id', - 'invoice_id' + 'invoice_id', + 'status_code' ] } id = db.Column(db.Integer, primary_key=True, autoincrement=True) invoice_id = db.Column(db.Integer, ForeignKey('invoices.id'), nullable=False, index=True) eft_credit_id = db.Column(db.Integer, ForeignKey('eft_credits.id'), nullable=False, index=True) + amount = db.Column(db.Numeric(19, 2), nullable=True) + status_code = db.Column('status_code', db.String(25), nullable=False, index=True) created_on = db.Column('created_on', db.DateTime, nullable=False, default=datetime.now) diff --git a/pay-api/src/pay_api/models/eft_process_status_code.py b/pay-api/src/pay_api/models/eft_process_status_code.py index fd8c4e6a0..c414a60d9 100644 --- a/pay-api/src/pay_api/models/eft_process_status_code.py +++ b/pay-api/src/pay_api/models/eft_process_status_code.py @@ -42,10 +42,11 @@ class EFTProcessStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(100), nullable=False) -class EFTProcessStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class EFTProcessStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = EFTProcessStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/eft_short_name_links.py b/pay-api/src/pay_api/models/eft_short_name_links.py new file mode 100644 index 000000000..f439c5873 --- /dev/null +++ b/pay-api/src/pay_api/models/eft_short_name_links.py @@ -0,0 +1,104 @@ +# Copyright © 2024 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Model to handle EFT short name to BCROS account mapping links.""" +from datetime import datetime +from _decimal import Decimal +from attrs import define + + +from sql_versioning import Versioned +from sqlalchemy import ForeignKey + +from .base_model import BaseModel +from .db import db + + +class EFTShortnameLinks(Versioned, BaseModel): # pylint: disable=too-many-instance-attributes + """This class manages the EFT short name links to auth account mapping.""" + + __tablename__ = 'eft_short_name_links' + # this mapper is used so that new and old versions of the service can be run simultaneously, + # making rolling upgrades easier + # This is used by SQLAlchemy to explicitly define which fields we're interested + # so it doesn't freak out and say it can't map the structure if other fields are present. + # This could occur from a failed deploy or during an upgrade. + # The other option is to tell SQLAlchemy to ignore differences, but that is ambiguous + # and can interfere with Alembic upgrades. + # + # NOTE: please keep mapper names in alpha-order, easier to track that way + # Exception, id is always first, _fields first + __mapper_args__ = { + 'include_properties': [ + 'id', + 'auth_account_id', + 'created_on', + 'eft_short_name_id', + 'status_code', + 'updated_by', + 'updated_by_name', + 'updated_on' + ] + } + + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + eft_short_name_id = db.Column(db.Integer, ForeignKey('eft_short_names.id'), nullable=False, index=True) + auth_account_id = db.Column('auth_account_id', db.String(50), nullable=False, index=True) + created_on = db.Column('created_on', db.DateTime, nullable=False, default=datetime.now) + status_code = db.Column('status_code', db.String(25), nullable=False, index=True) + updated_by = db.Column('updated_by', db.String(100), nullable=True) + updated_by_name = db.Column('updated_by_name', db.String(100), nullable=True) + updated_on = db.Column('updated_on', db.DateTime, nullable=True) + + @classmethod + def find_by_short_name_id(cls, short_name_id: id): + """Find by eft short name.""" + return cls.query.filter_by(eft_short_name_id=short_name_id).all() + + +@define +class EFTShortnameLinkSchema: # pylint: disable=too-few-public-methods + """Main schema used to serialize the EFT Short name link.""" + + id: int + short_name_id: str + status_code: str + account_id: str + account_name: str + account_branch: str + statement_id: str + amount_owing: Decimal + updated_by: str + updated_by_name: str + updated_on: datetime + has_pending_payment: bool + + @classmethod + def from_row(cls, row: EFTShortnameLinks): + """From row is used so we don't tightly couple to our database class. + + https://www.attrs.org/en/stable/init.html + """ + return cls(id=row.id, + short_name_id=row.eft_short_name_id, + status_code=row.status_code, + account_id=row.auth_account_id, + account_name=getattr(row, 'account_name', None), + account_branch=getattr(row, 'account_branch', None), + statement_id=getattr(row, 'latest_statement_id', None), + amount_owing=getattr(row, 'total_owing', None), + updated_by=row.updated_by, + updated_by_name=row.updated_by_name, + updated_on=row.updated_on, + has_pending_payment=bool(getattr(row, 'invoice_count', 0)) + ) diff --git a/pay-api/src/pay_api/models/eft_short_names.py b/pay-api/src/pay_api/models/eft_short_names.py index bae3ccc00..e9acbc11a 100644 --- a/pay-api/src/pay_api/models/eft_short_names.py +++ b/pay-api/src/pay_api/models/eft_short_names.py @@ -17,13 +17,14 @@ from attrs import define -from .base_model import VersionedModel +from sql_versioning import Versioned + +from .base_model import BaseModel from .db import db -from ..utils.util import cents_to_decimal -class EFTShortnames(VersionedModel): # pylint: disable=too-many-instance-attributes - """This class manages the EFT short name to auth account mapping.""" +class EFTShortnames(Versioned, BaseModel): # pylint: disable=too-many-instance-attributes + """This class manages the EFT short names.""" __tablename__ = 'eft_short_names' # this mapper is used so that new and old versions of the service can be run simultaneously, @@ -39,22 +40,14 @@ class EFTShortnames(VersionedModel): # pylint: disable=too-many-instance-attrib __mapper_args__ = { 'include_properties': [ 'id', - 'auth_account_id', 'created_on', - 'short_name', - 'linked_by', - 'linked_by_name', - 'linked_on' + 'short_name' ] } id = db.Column(db.Integer, primary_key=True, autoincrement=True) - auth_account_id = db.Column('auth_account_id', db.String(50), nullable=True, index=True) created_on = db.Column('created_on', db.DateTime, nullable=False, default=datetime.now) short_name = db.Column('short_name', db.String, nullable=False, index=True) - linked_by = db.Column('linked_by', db.String(100), nullable=True) - linked_by_name = db.Column('linked_by_name', db.String(100), nullable=True) - linked_on = db.Column('linked_on', db.DateTime, nullable=True) @classmethod def find_by_short_name(cls, short_name: str): @@ -67,18 +60,15 @@ class EFTShortnameSchema: # pylint: disable=too-few-public-methods """Main schema used to serialize the EFT Short name.""" id: int - short_name: str account_id: str account_name: str account_branch: str + amount_owing: Decimal created_on: datetime - transaction_id: int - transaction_date: datetime - deposit_date: datetime - deposit_amount: Decimal - linked_by: str - linked_by_name: str - linked_on: datetime + short_name: str + statement_id: int + status_code: str + cfs_account_status: str @classmethod def from_row(cls, row: EFTShortnames): @@ -87,16 +77,37 @@ def from_row(cls, row: EFTShortnames): https://www.attrs.org/en/stable/init.html """ return cls(id=row.id, - short_name=row.short_name, - account_id=row.auth_account_id, + account_id=getattr(row, 'auth_account_id', None), account_name=getattr(row, 'account_name', None), account_branch=getattr(row, 'account_branch', None), + amount_owing=getattr(row, 'total_owing', None), created_on=row.created_on, - transaction_id=getattr(row, 'transaction_id', None), - transaction_date=getattr(row, 'transaction_date', None), - deposit_date=getattr(row, 'deposit_date', None), - deposit_amount=cents_to_decimal(getattr(row, 'deposit_amount', None)), - linked_by=row.linked_by, - linked_by_name=row.linked_by_name, - linked_on=row.linked_on + short_name=row.short_name, + statement_id=getattr(row, 'latest_statement_id', None), + status_code=getattr(row, 'status_code', None), + cfs_account_status=getattr(row, 'cfs_account_status', None) + ) + + +@define +class EFTShortnameSummarySchema: + """Main schema used to serialize the EFT Short name summaries.""" + + id: int + short_name: str + last_payment_received_date: datetime + credits_remaining: Decimal + linked_accounts_count: int + + @classmethod + def from_row(cls, row: EFTShortnames): + """From row is used so we don't tightly couple to our database class. + + https://www.attrs.org/en/stable/init.html + """ + return cls(id=row.id, + short_name=row.short_name, + last_payment_received_date=getattr(row, 'last_payment_received_date', None), + credits_remaining=getattr(row, 'credits_remaining', None), + linked_accounts_count=getattr(row, 'linked_accounts_count', None) ) diff --git a/pay-api/src/pay_api/models/eft_transaction.py b/pay-api/src/pay_api/models/eft_transaction.py index aef3c82fd..3f4fbb68b 100644 --- a/pay-api/src/pay_api/models/eft_transaction.py +++ b/pay-api/src/pay_api/models/eft_transaction.py @@ -14,6 +14,7 @@ """Model to handle EFT file processing.""" from datetime import datetime from _decimal import Decimal + from attrs import define from sqlalchemy import ForeignKey, String @@ -21,7 +22,6 @@ from .base_model import BaseModel from .db import db -from ..utils.util import cents_to_decimal class EFTTransaction(BaseModel): # pylint: disable=too-many-instance-attributes @@ -81,13 +81,17 @@ class EFTTransaction(BaseModel): # pylint: disable=too-many-instance-attributes @define class EFTTransactionSchema: # pylint: disable=too-few-public-methods - """Main schema used to serialize a EFT Transaction.""" + """Main schema used to serialize an EFT Transaction.""" - id: int + transaction_id: int + account_id: str + account_name: str + account_branch: str + statement_id: int short_name_id: int transaction_date: datetime - deposit_date: datetime - deposit_amount: Decimal + transaction_amount: Decimal + transaction_description: str @classmethod def from_row(cls, row: EFTTransaction): @@ -95,8 +99,12 @@ def from_row(cls, row: EFTTransaction): https://www.attrs.org/en/stable/init.html """ - return cls(id=row.id, + return cls(transaction_id=row.transaction_id, short_name_id=row.short_name_id, + account_id=getattr(row, 'auth_account_id', None), + account_name=getattr(row, 'account_name', None), + account_branch=getattr(row, 'account_branch', None), + statement_id=getattr(row, 'statement_id', None), transaction_date=getattr(row, 'transaction_date', None), - deposit_date=getattr(row, 'deposit_date', None), - deposit_amount=cents_to_decimal(getattr(row, 'deposit_amount_cents', None))) + transaction_amount=getattr(row, 'transaction_amount', None), + transaction_description=getattr(row, 'transaction_description', None)) diff --git a/pay-api/src/pay_api/models/ejv_invoice_link.py b/pay-api/src/pay_api/models/ejv_link.py similarity index 80% rename from pay-api/src/pay_api/models/ejv_invoice_link.py rename to pay-api/src/pay_api/models/ejv_link.py index add18aa31..210803a5a 100644 --- a/pay-api/src/pay_api/models/ejv_invoice_link.py +++ b/pay-api/src/pay_api/models/ejv_link.py @@ -19,10 +19,10 @@ from .db import db -class EjvInvoiceLink(BaseModel): # pylint: disable=too-few-public-methods +class EjvLink(BaseModel): # pylint: disable=too-few-public-methods """This class manages linkages between EJV and invoices.""" - __tablename__ = 'ejv_invoice_links' + __tablename__ = 'ejv_links' # this mapper is used so that new and old versions of the service can be run simultaneously, # making rolling upgrades easier # This is used by SQLAlchemy to explicitly define which fields we're interested @@ -38,15 +38,22 @@ class EjvInvoiceLink(BaseModel): # pylint: disable=too-few-public-methods 'id', 'disbursement_status_code', 'ejv_header_id', - 'invoice_id', + 'link_id', + 'link_type', 'message', 'sequence' ] } id = db.Column(db.Integer, primary_key=True, autoincrement=True) - invoice_id = db.Column(db.Integer, ForeignKey('invoices.id'), nullable=False, index=True) - ejv_header_id = db.Column(db.Integer, ForeignKey('ejv_headers.id'), nullable=False, index=True) disbursement_status_code = db.Column(db.String(20), ForeignKey('disbursement_status_codes.code'), nullable=True) + ejv_header_id = db.Column(db.Integer, ForeignKey('ejv_headers.id'), nullable=False, index=True) + link_id = db.Column(db.Integer, nullable=True, index=True) # Repurposed for generic linking + link_type = db.Column(db.String(50), nullable=True, index=True) message = db.Column('message', db.String, nullable=True, index=False) sequence = db.Column(db.Integer, nullable=True) + + @classmethod + def find_ejv_link_by_link_id(cls, link_id: str): + """Return any ejv link by link_id.""" + return cls.query.filter_by(link_id=link_id).first() diff --git a/pay-api/src/pay_api/models/error_code.py b/pay-api/src/pay_api/models/error_code.py index bbf14d7aa..630f0d32d 100644 --- a/pay-api/src/pay_api/models/error_code.py +++ b/pay-api/src/pay_api/models/error_code.py @@ -48,12 +48,13 @@ class ErrorCode(db.Model, CodeTable): detail = db.Column(db.String(500)) -class ErrorCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class ErrorCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Error code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = ErrorCode + load_instance = True code = fields.String(data_key='type') diff --git a/pay-api/src/pay_api/models/fee_code.py b/pay-api/src/pay_api/models/fee_code.py index 8086da49f..5c5854173 100644 --- a/pay-api/src/pay_api/models/fee_code.py +++ b/pay-api/src/pay_api/models/fee_code.py @@ -56,11 +56,12 @@ def __str__(self): return f'{self.amount} ({self.code})' -class FeeCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class FeeCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Business.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = FeeCode + load_instance = True amount = fields.Float(data_key='amount') diff --git a/pay-api/src/pay_api/models/fee_schedule.py b/pay-api/src/pay_api/models/fee_schedule.py index 53787481d..044cc567a 100644 --- a/pay-api/src/pay_api/models/fee_schedule.py +++ b/pay-api/src/pay_api/models/fee_schedule.py @@ -130,7 +130,6 @@ def find_all(cls, corp_type_code: str = None, filing_type_code: str = None, desc query = query.filter_by(corp_type_code=corp_type_code) if description: - # TODO arrive at a better search descriptions = description.replace(' ', '%') query = query.join(CorpType, CorpType.code == FeeSchedule.corp_type_code). \ @@ -147,13 +146,15 @@ def save(self): db.session.commit() -class FeeScheduleSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class FeeScheduleSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Business.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = FeeSchedule + load_instance = True + exclude = ['distribution_codes'] # pylint: disable=no-member corp_type = ma.Nested(CorpTypeSchema, many=False, data_key='corp_type_code', diff --git a/pay-api/src/pay_api/models/filing_type.py b/pay-api/src/pay_api/models/filing_type.py index a981ea932..08d8c010a 100644 --- a/pay-api/src/pay_api/models/filing_type.py +++ b/pay-api/src/pay_api/models/filing_type.py @@ -54,10 +54,11 @@ def __str__(self): return f'{self.code}' -class FilingTypeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class FilingTypeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Business.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = FilingType + load_instance = True diff --git a/pay-api/src/pay_api/models/invoice.py b/pay-api/src/pay_api/models/invoice.py index a59329e1a..1627d5fd4 100644 --- a/pay-api/src/pay_api/models/invoice.py +++ b/pay-api/src/pay_api/models/invoice.py @@ -126,7 +126,7 @@ def update_invoices_for_revenue_updates(cls, fee_distribution_id: int): .join(PaymentLineItem, PaymentLineItem.invoice_id == Invoice.id) \ .filter(PaymentLineItem.fee_distribution_id == fee_distribution_id) - invoices: [Invoice] = query.all() + invoices: List[Invoice] = query.all() for invoice in invoices: if invoice.invoice_status_code == InvoiceStatus.PAID.value: invoice.invoice_status_code = InvoiceStatus.UPDATE_REVENUE_ACCOUNT.value @@ -199,8 +199,8 @@ class Meta(BaseSchema.Meta): # pylint: disable=too-few-public-methods many=False) _links = ma.Hyperlinks({ - 'self': ma.URLFor('INVOICE.get_invoice', invoice_id=''), - 'collection': ma.URLFor('INVOICE.get_invoices', invoice_id='') + 'self': ma.URLFor('INVOICE.get_invoice', values={'invoice_id': ''}), + 'collection': ma.URLFor('INVOICE.get_invoices', values={'invoice_id': ''}) }) total = fields.Float(data_key='total') @@ -224,7 +224,7 @@ def _clean_up(self, data, many): # pylint: disable=unused-argument if data.get('business_identifier', None) and data.get('business_identifier').startswith('T'): data.pop('business_identifier') - # TODO remove it later, adding this here to make non-breaking changes for other teams + # Adding this here to make non-breaking changes for other teams EG: CSO if data.get('status_code') == InvoiceStatus.PAID.value: data['status_code'] = PaymentStatus.COMPLETED.value diff --git a/pay-api/src/pay_api/models/invoice_reference_status_code.py b/pay-api/src/pay_api/models/invoice_reference_status_code.py index ca4129d25..998ae5892 100644 --- a/pay-api/src/pay_api/models/invoice_reference_status_code.py +++ b/pay-api/src/pay_api/models/invoice_reference_status_code.py @@ -42,10 +42,11 @@ class InvoiceReferenceStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class InvoiceReferenceStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class InvoiceReferenceStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = InvoiceReferenceStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/invoice_status_code.py b/pay-api/src/pay_api/models/invoice_status_code.py index cb7125678..696fa15d3 100644 --- a/pay-api/src/pay_api/models/invoice_status_code.py +++ b/pay-api/src/pay_api/models/invoice_status_code.py @@ -42,10 +42,11 @@ class InvoiceStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class InvoiceStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class InvoiceStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = InvoiceStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/line_item_status_code.py b/pay-api/src/pay_api/models/line_item_status_code.py index dcdb9e0a7..5fe9ea968 100644 --- a/pay-api/src/pay_api/models/line_item_status_code.py +++ b/pay-api/src/pay_api/models/line_item_status_code.py @@ -42,10 +42,11 @@ class LineItemStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class LineItemStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class LineItemStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = LineItemStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/notification_status_code.py b/pay-api/src/pay_api/models/notification_status_code.py index b6bcf7670..b86942693 100644 --- a/pay-api/src/pay_api/models/notification_status_code.py +++ b/pay-api/src/pay_api/models/notification_status_code.py @@ -42,10 +42,11 @@ class NotificationStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class NotificationStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class NotificationStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = NotificationStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/payment.py b/pay-api/src/pay_api/models/payment.py index df6a5611d..fc996bc5b 100644 --- a/pay-api/src/pay_api/models/payment.py +++ b/pay-api/src/pay_api/models/payment.py @@ -153,7 +153,6 @@ def search_account_payments(cls, auth_account_id: str, payment_status: str, page .outerjoin(Invoice, InvoiceReference.invoice_id == Invoice.id) \ .filter(PaymentAccount.auth_account_id == auth_account_id) - # TODO handle other status and conditions gracefully. if payment_status: query = query.filter(Payment.payment_status_code == payment_status) if payment_status == PaymentStatus.FAILED.value: @@ -165,7 +164,7 @@ def search_account_payments(cls, auth_account_id: str, payment_status: str, page # If call is to get NSF payments, get only active failed payments. # Exclude any payments which failed first and paid later. query = query.filter(or_(InvoiceReference.status_code == InvoiceReferenceStatus.ACTIVE.value, - Payment.cons_inv_number.in_(consolidated_inv_subquery))) + Payment.cons_inv_number.in_(consolidated_inv_subquery.select()))) query = query.order_by(Payment.id.asc()) pagination = query.paginate(per_page=limit, page=page) @@ -187,7 +186,7 @@ def find_payments_to_consolidate(cls, auth_account_id: str): .filter(InvoiceReference.status_code == InvoiceReferenceStatus.ACTIVE.value) \ .filter(PaymentAccount.auth_account_id == auth_account_id) \ .filter(or_(Payment.payment_status_code == PaymentStatus.FAILED.value, - Payment.invoice_number.in_(consolidated_inv_subquery))) + Payment.invoice_number.in_(consolidated_inv_subquery.select()))) return query.all() @@ -246,14 +245,14 @@ def search_purchase_history(cls, # noqa:E501; pylint:disable=too-many-arguments count = cls.get_count(auth_account_id, search_filter) # Add pagination sub_query = cls.generate_subquery(auth_account_id, search_filter, limit, page) - result = query.order_by(Invoice.id.desc()).filter(Invoice.id.in_(sub_query.subquery())).all() + result = query.order_by(Invoice.id.desc()).filter(Invoice.id.in_(sub_query.subquery().select())).all() # If maximum number of records is provided, return it as total if max_no_records > 0: count = max_no_records if max_no_records < count else count elif max_no_records > 0: # If maximum number of records is provided, set the page with that number sub_query = cls.generate_subquery(auth_account_id, search_filter, max_no_records, page=None) - result, count = query.filter(Invoice.id.in_(sub_query.subquery())).all(), sub_query.count() + result, count = query.filter(Invoice.id.in_(sub_query.subquery().select())).all(), sub_query.count() else: count = cls.get_count(auth_account_id, search_filter) if count > 60000: @@ -289,7 +288,7 @@ def get_count(cls, auth_account_id: str, search_filter: Dict): query = db.session.query(Invoice) \ .outerjoin(PaymentAccount, Invoice.payment_account_id == PaymentAccount.id) query = cls.filter(query, auth_account_id, search_filter, add_outer_joins=True) - count = query.group_by(Invoice.id).with_entities(func.count()).count() + count = query.group_by(Invoice.id).with_entities(func.count()).count() # pylint:disable=not-callable return count @classmethod @@ -313,7 +312,7 @@ def filter(cls, query, auth_account_id: str, search_filter: Dict, add_outer_join query = query.filter( Invoice.created_name.ilike(f'%{created_by}%')) # pylint: disable=no-member if created_name := search_filter.get('createdName', None): - query = query.filter(Invoice.created_name.ilike(f'%{created_name}%')) + query = query.filter(Invoice.created_name.ilike(f'%{created_name}%')) # pylint: disable=no-member if invoice_id := search_filter.get('id', None): query = query.filter(cast(Invoice.id, String).like(f'%{invoice_id}%')) diff --git a/pay-api/src/pay_api/models/payment_account.py b/pay-api/src/pay_api/models/payment_account.py index 31be79675..46bbc179f 100644 --- a/pay-api/src/pay_api/models/payment_account.py +++ b/pay-api/src/pay_api/models/payment_account.py @@ -16,13 +16,14 @@ from attrs import define from marshmallow import fields from sqlalchemy import Boolean, ForeignKey +from sql_versioning import Versioned -from .base_model import VersionedModel +from .base_model import BaseModel from .db import db from .base_schema import BaseSchema -class PaymentAccount(VersionedModel): # pylint: disable=too-many-instance-attributes +class PaymentAccount(Versioned, BaseModel): # pylint: disable=too-many-instance-attributes """This class manages all of the base data about Payment Account.""" __tablename__ = 'payment_accounts' @@ -44,9 +45,6 @@ class PaymentAccount(VersionedModel): # pylint: disable=too-many-instance-attri 'bcol_user_id', 'billable', 'branch_name', - 'created_by', - 'created_name', - 'created_on', 'credit', 'eft_enable', 'name', @@ -54,10 +52,7 @@ class PaymentAccount(VersionedModel): # pylint: disable=too-many-instance-attri 'pad_tos_accepted_by', 'pad_tos_accepted_date', 'payment_method', - 'statement_notification_enabled', - 'updated_by', - 'updated_name', - 'updated_on' + 'statement_notification_enabled' ] } diff --git a/pay-api/src/pay_api/models/payment_line_item.py b/pay-api/src/pay_api/models/payment_line_item.py index 271ce2517..62635f4a5 100644 --- a/pay-api/src/pay_api/models/payment_line_item.py +++ b/pay-api/src/pay_api/models/payment_line_item.py @@ -86,7 +86,7 @@ def find_by_invoice_ids(cls, invoice_ids: list): PaymentLineItem.invoice_id.desc()).all() -class PaymentLineItemSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class PaymentLineItemSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Payment line item.""" class Meta: # pylint: disable=too-few-public-methods @@ -94,6 +94,7 @@ class Meta: # pylint: disable=too-few-public-methods model = PaymentLineItem exclude = ['fee_schedule_id', 'fee_schedule'] + load_instance = True line_item_status_code = fields.String(data_key='status_code') filing_fees = fields.Float(data_key='filing_fees') diff --git a/pay-api/src/pay_api/models/payment_method.py b/pay-api/src/pay_api/models/payment_method.py index 8ea20cc65..ed9b4edb7 100644 --- a/pay-api/src/pay_api/models/payment_method.py +++ b/pay-api/src/pay_api/models/payment_method.py @@ -47,10 +47,11 @@ def save(self): db.session.commit() -class PaymentMethodSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class PaymentMethodSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the System Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = PaymentMethod + load_instance = True diff --git a/pay-api/src/pay_api/models/payment_status_code.py b/pay-api/src/pay_api/models/payment_status_code.py index 339a8c9dc..bb4f92378 100644 --- a/pay-api/src/pay_api/models/payment_status_code.py +++ b/pay-api/src/pay_api/models/payment_status_code.py @@ -42,10 +42,11 @@ class PaymentStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class PaymentStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class PaymentStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = PaymentStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/payment_system.py b/pay-api/src/pay_api/models/payment_system.py index 6974609c3..91a0ab6b7 100644 --- a/pay-api/src/pay_api/models/payment_system.py +++ b/pay-api/src/pay_api/models/payment_system.py @@ -47,10 +47,11 @@ def save(self): db.session.commit() -class PaymentSystemSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class PaymentSystemSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the System Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = PaymentSystem + load_instance = True diff --git a/pay-api/src/pay_api/models/receipt.py b/pay-api/src/pay_api/models/receipt.py index 8884b2611..a9f617f94 100644 --- a/pay-api/src/pay_api/models/receipt.py +++ b/pay-api/src/pay_api/models/receipt.py @@ -67,12 +67,13 @@ def find_all_receipts_for_invoice(cls, invoice_id: int): return cls.query.filter_by(invoice_id=invoice_id).all() -class ReceiptSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class ReceiptSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Receipt.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = Receipt + load_instance = True receipt_date = fields.DateTime(tzinfo=pytz.timezone(LEGISLATIVE_TIMEZONE)) diff --git a/pay-api/src/pay_api/models/refund.py b/pay-api/src/pay_api/models/refund.py index 9ad64d34f..bba73faed 100644 --- a/pay-api/src/pay_api/models/refund.py +++ b/pay-api/src/pay_api/models/refund.py @@ -80,10 +80,11 @@ def find_by_routing_slip_id(cls, routing_slip_id: int): return cls.query.filter_by(routing_slip_id=routing_slip_id).one_or_none() -class RefundSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class RefundSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Refund.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = Refund + load_instance = True diff --git a/pay-api/src/pay_api/models/refunds_partial.py b/pay-api/src/pay_api/models/refunds_partial.py index 9fda23d51..8147ba687 100644 --- a/pay-api/src/pay_api/models/refunds_partial.py +++ b/pay-api/src/pay_api/models/refunds_partial.py @@ -15,15 +15,16 @@ from decimal import Decimal from attrs import define +from sql_versioning import Versioned from sqlalchemy import ForeignKey from .audit import Audit -from .base_model import VersionedModel +from .base_model import BaseModel from .db import db from ..utils.enums import RefundsPartialType -class RefundsPartial(Audit, VersionedModel): # pylint: disable=too-many-instance-attributes +class RefundsPartial(Audit, Versioned, BaseModel): # pylint: disable=too-many-instance-attributes """This class manages the data for payment line item partial refunds.""" __tablename__ = 'refunds_partial' @@ -40,11 +41,17 @@ class RefundsPartial(Audit, VersionedModel): # pylint: disable=too-many-instanc __mapper_args__ = { 'include_properties': [ 'id', + 'created_by', + 'created_on', + 'created_name', + 'disbursement_status_code', + 'disbursement_date', 'payment_line_item_id', 'refund_amount', 'refund_type', - 'disbursement_status_code', - 'disbursement_date' + 'updated_by', + 'updated_on', + 'updated_name' ] } diff --git a/pay-api/src/pay_api/models/routing_slip.py b/pay-api/src/pay_api/models/routing_slip.py index bdafd07c8..a6c3163fe 100644 --- a/pay-api/src/pay_api/models/routing_slip.py +++ b/pay-api/src/pay_api/models/routing_slip.py @@ -195,7 +195,7 @@ def search(cls, search_filter: Dict, # pylint: disable=too-many-arguments, too- query = cls._add_date_filter(query, search_filter) if initiator := search_filter.get('initiator', None): - query = query.filter(RoutingSlip.created_name.ilike('%' + initiator + '%')) + query = query.filter(RoutingSlip.created_name.ilike('%' + initiator + '%')) # pylint: disable=no-member if business_identifier := search_filter.get('businessIdentifier', None): query = query.filter(Invoice.business_identifier == business_identifier) @@ -215,7 +215,7 @@ def search(cls, search_filter: Dict, # pylint: disable=too-many-arguments, too- limit(limit).\ offset((page - 1) * limit).\ subquery() - query = query.filter(RoutingSlip.id.in_(sub_query)) + query = query.filter(RoutingSlip.id.in_(sub_query.select())) result = query.all() count = len(result) diff --git a/pay-api/src/pay_api/models/routing_slip_status_code.py b/pay-api/src/pay_api/models/routing_slip_status_code.py index 13936767c..9f7273aed 100644 --- a/pay-api/src/pay_api/models/routing_slip_status_code.py +++ b/pay-api/src/pay_api/models/routing_slip_status_code.py @@ -42,10 +42,11 @@ class RoutingSlipStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class RoutingSlipStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class RoutingSlipStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = RoutingSlipStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/statement.py b/pay-api/src/pay_api/models/statement.py index 4863ed6d2..6899525bd 100644 --- a/pay-api/src/pay_api/models/statement.py +++ b/pay-api/src/pay_api/models/statement.py @@ -13,12 +13,12 @@ # limitations under the License. """Model to handle statements data.""" +from datetime import datetime import pytz from marshmallow import fields -from sqlalchemy import ForeignKey, and_, case, func, literal_column, or_ +from sql_versioning import history_cls +from sqlalchemy import ForeignKey, and_, case, literal_column from sqlalchemy.ext.hybrid import hybrid_property -from sqlalchemy.orm import aliased -from sqlalchemy_continuum import transaction_class, version_class from pay_api.utils.constants import LEGISLATIVE_TIMEZONE from pay_api.utils.enums import StatementFrequency @@ -72,22 +72,46 @@ class Statement(BaseModel): @hybrid_property def payment_methods(self): """Return all payment methods that were active during the statement period based on payment account versions.""" - payment_account_version = version_class(PaymentAccount) - transaction_start = aliased(transaction_class(PaymentAccount)) - transaction_end = aliased(transaction_class(PaymentAccount)) - - subquery = db.session.query(func.array_agg(func.DISTINCT(payment_account_version.payment_method)) - .label('payment_methods'))\ - .join(Statement, Statement.payment_account_id == payment_account_version.id)\ - .join(transaction_start, payment_account_version.transaction_id == transaction_start.id)\ - .outerjoin(transaction_end, payment_account_version.end_transaction_id == transaction_end.id)\ - .filter(payment_account_version.id == self.payment_account_id) \ - .filter(and_(Statement.id == self.id, transaction_start.issued_at <= Statement.to_date, - or_(transaction_end.issued_at >= Statement.from_date, - transaction_end.id.is_(None))))\ - .group_by(Statement.id).first() - - return subquery[0] if subquery else [] + payment_account = PaymentAccount.find_by_id(self.payment_account_id) + payment_account_history_class = history_cls(PaymentAccount) + payment_account_history = db.session.query(payment_account_history_class) \ + .join(Statement, Statement.payment_account_id == payment_account_history_class.id) \ + .filter(payment_account_history_class.id == self.payment_account_id) \ + .filter(Statement.id == self.id) \ + .order_by(payment_account_history_class.changed.asc()) \ + .all() + + # The code below combines the history rows with the current state of payment_account. + # This is necessary because the new versioning doesn't have from and to dates, only changed. + # It is possible to handle this through SQL using LEAD and LAG functions. + # Since the volume of rows is low, the pythonic approach should be sufficient. + history_ranges = [ + { + 'from_date': datetime.min.date() if idx == 0 else payment_account_history[idx - 1].changed.date(), + 'to_date': historical.changed.date(), + 'payment_method': payment_account.payment_method if idx == len(payment_account_history) - 1 + else historical.payment_method + } + for idx, historical in enumerate(payment_account_history) + ] + + history_ranges.append({ + 'from_date': payment_account_history[-1].changed.date() if payment_account_history else datetime.min.date(), + 'to_date': datetime.max.date(), + 'payment_method': payment_account.payment_method + }) + + payment_methods = { + history_item['payment_method'] + for history_item in history_ranges + if ( + history_item['from_date'] <= self.from_date <= history_item['to_date'] or + history_item['from_date'] <= self.to_date <= history_item['to_date'] or + self.from_date <= history_item['from_date'] <= self.to_date <= history_item['to_date'] + ) + } + + return list(payment_methods) @classmethod def find_all_statements_for_account(cls, auth_account_id: str, page, limit): @@ -98,20 +122,18 @@ def find_all_statements_for_account(cls, auth_account_id: str, page, limit): PaymentAccount.auth_account_id == auth_account_id)) frequency_case = case( - [ - ( - Statement.frequency == StatementFrequency.MONTHLY.value, - literal_column("'1'") - ), - ( - Statement.frequency == StatementFrequency.WEEKLY.value, - literal_column("'2'") - ), - ( - Statement.frequency == StatementFrequency.DAILY.value, - literal_column("'3'") - ) - ], + ( + Statement.frequency == StatementFrequency.MONTHLY.value, + literal_column("'1'") + ), + ( + Statement.frequency == StatementFrequency.WEEKLY.value, + literal_column("'2'") + ), + ( + Statement.frequency == StatementFrequency.DAILY.value, + literal_column("'3'") + ), else_=literal_column("'4'") ) @@ -138,13 +160,14 @@ def find_all_payments_and_invoices_for_statement(cls, statement_id: str): return query.all() -class StatementSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class StatementSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Statements.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = Statement + load_instance = True from_date = fields.Date(tzinfo=pytz.timezone(LEGISLATIVE_TIMEZONE)) to_date = fields.Date(tzinfo=pytz.timezone(LEGISLATIVE_TIMEZONE)) diff --git a/pay-api/src/pay_api/models/statement_invoices.py b/pay-api/src/pay_api/models/statement_invoices.py index df3fc8816..92060cb53 100644 --- a/pay-api/src/pay_api/models/statement_invoices.py +++ b/pay-api/src/pay_api/models/statement_invoices.py @@ -55,10 +55,11 @@ def find_all_invoices_for_statement(cls, statement_identifier: str): return cls.query.filter_by(statement_id=statement_identifier).all() -class StatementInvoicesSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class StatementInvoicesSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Statements.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = StatementInvoices + load_instance = True diff --git a/pay-api/src/pay_api/models/statement_recipients.py b/pay-api/src/pay_api/models/statement_recipients.py index fe5b74965..531fffd62 100644 --- a/pay-api/src/pay_api/models/statement_recipients.py +++ b/pay-api/src/pay_api/models/statement_recipients.py @@ -58,7 +58,7 @@ def find_all_recipients(cls, auth_account_id: str): """Return all active recipients for an account.""" return cls.query \ .join(PaymentAccount) \ - .filter(PaymentAccount.auth_account_id == auth_account_id).all() + .filter(PaymentAccount.auth_account_id == str(auth_account_id)).all() @classmethod def find_all_recipients_for_payment_id(cls, payment_account_id: str): @@ -79,10 +79,11 @@ def bulk_save_recipients(cls, recipients: list): BaseModel.commit() -class StatementRecipientsSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class StatementRecipientsSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Payment Account.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = StatementRecipients + load_instance = True diff --git a/pay-api/src/pay_api/models/statement_settings.py b/pay-api/src/pay_api/models/statement_settings.py index 137e1c687..325b31b80 100644 --- a/pay-api/src/pay_api/models/statement_settings.py +++ b/pay-api/src/pay_api/models/statement_settings.py @@ -87,10 +87,11 @@ def find_accounts_settings_by_frequency(cls, valid_date: datetime, frequency: St return query.all() -class StatementSettingsSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class StatementSettingsSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Statements settings.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = StatementSettings + load_instance = True diff --git a/pay-api/src/pay_api/models/transaction_status_code.py b/pay-api/src/pay_api/models/transaction_status_code.py index 24f74001c..615a1c9ae 100644 --- a/pay-api/src/pay_api/models/transaction_status_code.py +++ b/pay-api/src/pay_api/models/transaction_status_code.py @@ -42,10 +42,11 @@ class TransactionStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class TransactionStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class TransactionStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = TransactionStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/resources/ops.py b/pay-api/src/pay_api/resources/ops.py index 8856e9f60..c399771a1 100755 --- a/pay-api/src/pay_api/resources/ops.py +++ b/pay-api/src/pay_api/resources/ops.py @@ -27,7 +27,7 @@ def get_ops_healthz(): """Return a JSON object stating the health of the Service and dependencies.""" try: - db.engine.execute(SQL) + db.session.execute(SQL) except exc.SQLAlchemyError: return {'message': 'api is down'}, 500 diff --git a/pay-api/src/pay_api/resources/v1/account.py b/pay-api/src/pay_api/resources/v1/account.py index 36facf26e..ad54e40ea 100644 --- a/pay-api/src/pay_api/resources/v1/account.py +++ b/pay-api/src/pay_api/resources/v1/account.py @@ -28,7 +28,6 @@ from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import CfsAccountStatus, ContentType, Role from pay_api.utils.errors import Error -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('ACCOUNTS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/accounts') @@ -79,7 +78,6 @@ def get_eft_accounts(): @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'PUT', 'DELETE']) -@_tracing.trace() @_jwt.requires_auth def get_account(account_number: str): """Get payment account details.""" @@ -93,7 +91,6 @@ def get_account(account_number: str): @bp.route('//eft', methods=['PATCH']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.SYSTEM.value]) def patch_account(account_number: str): """Enable eft for an account.""" @@ -110,7 +107,6 @@ def patch_account(account_number: str): @bp.route('/', methods=['PUT']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.SYSTEM.value]) def put_account(account_number: str): """Update the payment account records.""" @@ -137,7 +133,6 @@ def put_account(account_number: str): @bp.route('/', methods=['DELETE']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.SYSTEM.value]) def delete_account(account_number: str): """Delete payment account details.""" @@ -228,7 +223,6 @@ def put_account_fee_product(account_number: str, product: str): @bp.route('//payments/queries', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() @_jwt.requires_auth def post_search_purchase_history(account_number: str): """Search purchase history.""" @@ -258,7 +252,6 @@ def post_search_purchase_history(account_number: str): @bp.route('//payments/reports', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() @_jwt.requires_auth def post_account_purchase_report(account_number: str): """Create the account purchase report.""" diff --git a/pay-api/src/pay_api/resources/v1/account_statements.py b/pay-api/src/pay_api/resources/v1/account_statements.py index 2a08174f7..8559819d7 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements.py +++ b/pay-api/src/pay_api/resources/v1/account_statements.py @@ -24,7 +24,6 @@ from pay_api.utils.constants import EDIT_ROLE from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import ContentType -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('ACCOUNT_STATEMENTS', __name__, @@ -33,7 +32,6 @@ @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() @_jwt.requires_auth def get_account_statements(account_id): """Get all statements records for an account.""" @@ -52,7 +50,6 @@ def get_account_statements(account_id): @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() @_jwt.requires_auth def get_account_statement(account_id: str, statement_id: str): """Create the statement report.""" @@ -74,7 +71,6 @@ def get_account_statement(account_id: str, statement_id: str): @bp.route('/summary', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() @_jwt.requires_auth def get_account_statement_summary(account_id: str): """Create the statement report.""" diff --git a/pay-api/src/pay_api/resources/v1/account_statements_notifications.py b/pay-api/src/pay_api/resources/v1/account_statements_notifications.py index 0984d22fd..0c8dba6ef 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements_notifications.py +++ b/pay-api/src/pay_api/resources/v1/account_statements_notifications.py @@ -25,7 +25,6 @@ from pay_api.utils.constants import CHANGE_STATEMENT_SETTINGS, EDIT_ROLE from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.errors import Error -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('ACCOUNT_NOTIFICATIONS', __name__, @@ -34,7 +33,6 @@ @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.requires_auth def get_account_notifications(account_id): """Get all statements records for an account.""" @@ -50,7 +48,6 @@ def get_account_notifications(account_id): @bp.route('', methods=['POST']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.requires_auth def post_account_notification(account_id): """Update the statement settings .""" diff --git a/pay-api/src/pay_api/resources/v1/account_statements_settings.py b/pay-api/src/pay_api/resources/v1/account_statements_settings.py index 9080024d9..60cdc4335 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements_settings.py +++ b/pay-api/src/pay_api/resources/v1/account_statements_settings.py @@ -23,7 +23,6 @@ from pay_api.utils.auth import jwt as _jwt from pay_api.utils.constants import CHANGE_STATEMENT_SETTINGS, EDIT_ROLE from pay_api.utils.endpoints_enums import EndpointEnum -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('ACCOUNT_SETTINGS', __name__, @@ -32,7 +31,6 @@ @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.requires_auth def get_account_statement_settings(account_id): """Get all statements records for an account.""" @@ -48,7 +46,6 @@ def get_account_statement_settings(account_id): @bp.route('', methods=['POST']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.requires_auth def post_account_statement_settings(account_id): """Update the statement settings .""" diff --git a/pay-api/src/pay_api/resources/v1/code.py b/pay-api/src/pay_api/resources/v1/code.py index 595962765..a578bd1e8 100644 --- a/pay-api/src/pay_api/resources/v1/code.py +++ b/pay-api/src/pay_api/resources/v1/code.py @@ -18,7 +18,6 @@ from flask_cors import cross_origin from pay_api.services.code import Code as CodeService -from pay_api.utils.trace import tracing as _tracing from pay_api.utils.endpoints_enums import EndpointEnum @@ -27,7 +26,6 @@ @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() def get_codes_by_type(code_type): """Return all codes based on code_type.""" return CodeService.find_code_values_by_type(code_type), HTTPStatus.OK @@ -35,7 +33,6 @@ def get_codes_by_type(code_type): @bp.route('//', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() def get_code(code_type, code): """Return all codes based on code_type.""" return CodeService.find_code_value_by_type_and_code(code_type, code), HTTPStatus.OK diff --git a/pay-api/src/pay_api/resources/v1/distributions.py b/pay-api/src/pay_api/resources/v1/distributions.py index 1916ebff8..3f9c1a94b 100644 --- a/pay-api/src/pay_api/resources/v1/distributions.py +++ b/pay-api/src/pay_api/resources/v1/distributions.py @@ -24,7 +24,6 @@ from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role from pay_api.utils.errors import Error -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('DISTRIBUTIONS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/fees/distributions') @@ -32,7 +31,6 @@ @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'PUT']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) def get_fee_distribution(distribution_code_id: int): """Return distribution by provided id.""" @@ -49,7 +47,6 @@ def get_fee_distribution(distribution_code_id: int): @bp.route('/', methods=['PUT']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) def put_fee_distribution(distribution_code_id: int): """Update distribution from the payload.""" @@ -71,7 +68,6 @@ def put_fee_distribution(distribution_code_id: int): @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) def get_fee_distributions(): """Return all distributions.""" @@ -88,7 +84,6 @@ def get_fee_distributions(): @bp.route('', methods=['POST']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) def post_fee_distribution(): """Create a new distribution from the payload.""" @@ -110,7 +105,6 @@ def post_fee_distribution(): @bp.route('//schedules', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) def get_fee_distribution_schedules(distribution_code_id: int): """Return all fee schedules linked to the distribution.""" @@ -127,7 +121,6 @@ def get_fee_distribution_schedules(distribution_code_id: int): @bp.route('//schedules', methods=['POST']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) def post_fee_distribution_schedule(distribution_code_id: int): """Create link between distribution and fee schedule.""" diff --git a/pay-api/src/pay_api/resources/v1/eft_short_names.py b/pay-api/src/pay_api/resources/v1/eft_short_names.py index b55416988..0be018dd7 100644 --- a/pay-api/src/pay_api/resources/v1/eft_short_names.py +++ b/pay-api/src/pay_api/resources/v1/eft_short_names.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. """Resource for EFT Short name.""" -from decimal import Decimal from http import HTTPStatus from flask import Blueprint, current_app, jsonify, request @@ -20,15 +19,14 @@ from pay_api.exceptions import BusinessException from pay_api.services.eft_short_names import EFTShortnames as EFTShortnameService +from pay_api.services.eft_short_name_summaries import EFTShortnameSummaries as EFTShortnameSummariesService from pay_api.services.eft_short_names import EFTShortnamesSearch from pay_api.services.eft_transactions import EFTTransactions as EFTTransactionService from pay_api.services.eft_transactions import EFTTransactionSearch from pay_api.utils.auth import jwt as _jwt from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role -from pay_api.utils.trace import tracing as _tracing -from pay_api.utils.util import string_to_date - +from pay_api.utils.util import string_to_date, string_to_decimal, string_to_int bp = Blueprint('EFT_SHORT_NAMES', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/eft-shortnames') @@ -41,42 +39,65 @@ def get_eft_shortnames(): """Get all eft short name records.""" current_app.logger.info('get_eft_shortnames') + + return jsonify(response), status + + +@bp.route('/summaries', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET']) +@_jwt.requires_auth +@_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) +def get_eft_shortname_summaries(): + """Get all eft short name summaries.""" + current_app.logger.info('get_eft_shortname_summaries') return jsonify(response), status @bp.route('/', methods=['GET', 'OPTIONS']) -@cross_origin(origins='*', methods=['GET', 'PATCH']) -@_tracing.trace() +@cross_origin(origins='*', methods=['GET']) @_jwt.requires_auth @_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) def get_eft_shortname(short_name_id: int): @@ -84,51 +105,87 @@ def get_eft_shortname(short_name_id: int): current_app.logger.info('get_eft_shortname') return jsonify(response), status -@bp.route('/', methods=['PATCH']) +@bp.route('//transactions', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET']) +@_jwt.requires_auth +@_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) +def get_eft_shortname_transactions(short_name_id: int): + """Get EFT short name transactions.""" + current_app.logger.info('get_eft_shortname_transactions') + return jsonify(response), status + + +@bp.route('//links', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'POST']) +@_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) +def get_eft_shortname_links(short_name_id: int): + """Get EFT short name account links.""" + current_app.logger.info('get_eft_shortname_links') + return jsonify(response), status + + +@bp.route('//links', methods=['POST']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) -def patch_eft_shortname(short_name_id: int): - """Update EFT short name mapping.""" - current_app.logger.info('patch_eft_shortname') + current_app.logger.debug('>post_eft_shortname_link') return jsonify(response), status -@bp.route('//transactions', methods=['GET', 'OPTIONS']) -@cross_origin(origins='*', methods=['GET']) -@_tracing.trace() -@_jwt.requires_auth +@bp.route('//links/', methods=['DELETE', 'OPTIONS']) +@cross_origin(origins='*', methods=['DELETE']) @_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) -def get_eft_shortname_transactions(short_name_id: int): - """Get EFT short name transactions.""" - current_app.logger.info('get_eft_shortname_transactions') + current_app.logger.debug('>delete_eft_shortname_link') return jsonify(response), status diff --git a/pay-api/src/pay_api/resources/v1/fas/routing_slip.py b/pay-api/src/pay_api/resources/v1/fas/routing_slip.py index efb9638ce..f8311e8f5 100644 --- a/pay-api/src/pay_api/resources/v1/fas/routing_slip.py +++ b/pay-api/src/pay_api/resources/v1/fas/routing_slip.py @@ -24,7 +24,6 @@ from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role from pay_api.utils.errors import Error -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('FAS_ROUTING_SLIPS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/fas/routing-slips') @@ -32,7 +31,6 @@ @bp.route('', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_CREATE.value]) def post_routing_slip(): """Create routing slip.""" @@ -54,7 +52,6 @@ def post_routing_slip(): @bp.route('/queries', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_SEARCH.value]) def post_search_routing_slips(): """Get routing slips.""" @@ -79,7 +76,6 @@ def post_search_routing_slips(): @bp.route('//reports', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_REPORTS.value]) def post_routing_slip_report(date: str): """Create routing slip report.""" @@ -98,7 +94,6 @@ def post_routing_slip_report(date: str): @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'PATCH']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_VIEW.value]) def get_routing_slip(routing_slip_number: str): """Get routing slip.""" @@ -118,7 +113,6 @@ def get_routing_slip(routing_slip_number: str): @bp.route('/', methods=['PATCH']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_EDIT.value]) def patch_routing_slip(routing_slip_number: str): """Patch routing slip.""" @@ -135,7 +129,6 @@ def patch_routing_slip(routing_slip_number: str): @bp.route('//links', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_VIEW.value, Role.FAS_LINK.value]) def get_routing_slip_links(routing_slip_number: str): """Get routing slip links ;ie parent/child details.""" @@ -152,7 +145,6 @@ def get_routing_slip_links(routing_slip_number: str): @bp.route('/links', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_LINK.value]) def post_routing_slip_link(): """Get routing slip links ;ie parent/child details.""" @@ -176,7 +168,6 @@ def post_routing_slip_link(): @bp.route('//comments', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) @_jwt.has_one_of_roles([Role.FAS_VIEW.value]) -@_tracing.trace() def get_routing_slip_comments(routing_slip_number: str): """Get comments for a slip.""" current_app.logger.info('/comments', methods=['POST']) @cross_origin(origins='*') @_jwt.has_one_of_roles([Role.FAS_VIEW.value]) -@_tracing.trace() def post_routing_slip_comment(routing_slip_number: str): """Create comment for a slip.""" current_app.logger.info('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.VIEWER.value, Role.EDITOR.value, Role.STAFF.value]) def get_fee_by_corp_and_filing_type(corp_type, filing_type_code): """Calculate the fee for the filing using the corp type/filing type and return fee.""" diff --git a/pay-api/src/pay_api/resources/v1/fee_schedule.py b/pay-api/src/pay_api/resources/v1/fee_schedule.py index 24d019e2c..1a11e6d4d 100644 --- a/pay-api/src/pay_api/resources/v1/fee_schedule.py +++ b/pay-api/src/pay_api/resources/v1/fee_schedule.py @@ -20,14 +20,12 @@ from pay_api.exceptions import BusinessException from pay_api.services import FeeSchedule from pay_api.utils.endpoints_enums import EndpointEnum -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('FEE_SCHEDULE', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/fees/schedules') @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() def get_fee_schedules(): """Calculate the fee for the filing using the corp type/filing type and return fee.""" try: diff --git a/pay-api/src/pay_api/resources/v1/invoice.py b/pay-api/src/pay_api/resources/v1/invoice.py index 496cecf64..f04a883cc 100644 --- a/pay-api/src/pay_api/resources/v1/invoice.py +++ b/pay-api/src/pay_api/resources/v1/invoice.py @@ -27,7 +27,6 @@ from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role from pay_api.utils.errors import Error -from pay_api.utils.trace import tracing as _tracing from pay_api.utils.util import get_str_by_path bp = Blueprint('INVOICE', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/payment-requests') @@ -35,7 +34,6 @@ @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.requires_roles([Role.SYSTEM.value]) def get_invoices(): """Get the invoice records.""" @@ -51,7 +49,6 @@ def get_invoices(): @bp.route('', methods=['POST']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.requires_auth def post_invoice(): """Create the payment request records.""" @@ -79,7 +76,6 @@ def post_invoice(): @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'DELETE', 'PATCH']) -@_tracing.trace() @_jwt.requires_auth def get_invoice(invoice_id): """Get the invoice records.""" @@ -92,7 +88,6 @@ def get_invoice(invoice_id): @bp.route('/', methods=['DELETE']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.requires_auth def delete_invoice(invoice_id): """Soft delete the invoice records.""" @@ -112,7 +107,6 @@ def delete_invoice(invoice_id): @bp.route('/', methods=['PATCH']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.requires_auth def patch_invoice(invoice_id: int = None): """Update the payment method for an online banking .""" @@ -139,7 +133,6 @@ def patch_invoice(invoice_id: int = None): @bp.route('//reports', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) @_jwt.requires_auth -@_tracing.trace() def post_invoice_report(invoice_id: int = None): """Update the payment method for an online banking .""" current_app.logger.info('/payments') @@ -34,7 +33,6 @@ @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.requires_auth def get_account_payments(account_id: str): """Get account payments.""" @@ -52,7 +50,6 @@ def get_account_payments(account_id: str): @bp.route('', methods=['POST']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.requires_auth def post_account_payment(account_id: str): """Create account payments.""" diff --git a/pay-api/src/pay_api/resources/v1/transaction.py b/pay-api/src/pay_api/resources/v1/transaction.py index 7f0672f65..23378f26e 100644 --- a/pay-api/src/pay_api/resources/v1/transaction.py +++ b/pay-api/src/pay_api/resources/v1/transaction.py @@ -23,7 +23,6 @@ from pay_api.utils.auth import jwt as _jwt from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.errors import Error -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('TRANSACTIONS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}') @@ -31,7 +30,6 @@ @bp.route('/payment-requests//transactions', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.requires_auth def get_transactions(invoice_id): """Get all transaction records for a invoice.""" @@ -44,7 +42,6 @@ def get_transactions(invoice_id): @bp.route('/payment-requests//transactions', methods=['POST']) @bp.route('/payments//transactions', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() def post_transaction(invoice_id: int = None, payment_id: int = None): """Create the Transaction records.""" current_app.logger.info('/transactions/', methods=['GET', 'OPTIONS']) @bp.route('/payments//transactions/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'PATCH']) -@_tracing.trace() @_jwt.requires_auth def get_transaction(invoice_id: int = None, payment_id: int = None, transaction_id=None): """Get the Transaction record.""" @@ -94,7 +90,6 @@ def get_transaction(invoice_id: int = None, payment_id: int = None, transaction_ @bp.route('/payment-requests//transactions/', methods=['PATCH']) @bp.route('/payments//transactions/', methods=['PATCH']) @cross_origin(origins='*') -@_tracing.trace() def patch_transaction(invoice_id: int = None, payment_id: int = None, transaction_id=None): """Update the transaction record by querying payment system.""" current_app.logger.info(' InvoiceReference: - """Return a static invoice number for direct pay.""" - current_app.logger.debug('find_fee_schedules_by_distribution_id') return data diff --git a/pay-api/src/pay_api/services/eft_service.py b/pay-api/src/pay_api/services/eft_service.py index 5e9e8617b..3e2b51cd4 100644 --- a/pay-api/src/pay_api/services/eft_service.py +++ b/pay-api/src/pay_api/services/eft_service.py @@ -13,15 +13,17 @@ # limitations under the License. """Service to manage CFS EFT Payments.""" from datetime import datetime +from typing import Any, Dict, List from flask import current_app +from pay_api.models import CfsAccount as CfsAccountModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import InvoiceReference as InvoiceReferenceModel from pay_api.models import Payment as PaymentModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Receipt as ReceiptModel -from pay_api.utils.enums import InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus +from pay_api.utils.enums import CfsAccountStatus, InvoiceReferenceStatus, PaymentMethod, PaymentStatus, PaymentSystem from .deposit_service import DepositService from .invoice import Invoice @@ -34,13 +36,36 @@ class EftService(DepositService): """Service to manage electronic fund transfers.""" def get_payment_method_code(self): - """Return EFT as the system code.""" + """Return EFT as the payment method code.""" return PaymentMethod.EFT.value - def create_invoice(self, payment_account: PaymentAccount, line_items: [PaymentLineItem], invoice: Invoice, - **kwargs) -> InvoiceReference: - """Return a static invoice number for direct pay.""" - # Do nothing here as the invoice references will be created later for eft payment reconciliations (TDI17). + def get_payment_system_code(self): + """Return PAYBC as the system code.""" + return PaymentSystem.PAYBC.value + + def create_account(self, identifier: str, contact_info: Dict[str, Any], payment_info: Dict[str, Any], + **kwargs) -> CfsAccountModel: + """Create an account for the EFT transactions.""" + # Create CFS Account model instance, set the status as PENDING + current_app.logger.info(f'Creating EFT account details in PENDING status for {identifier}') + cfs_account = CfsAccountModel() + cfs_account.status = CfsAccountStatus.PENDING.value + return cfs_account + + def update_account(self, name: str, cfs_account: CfsAccountModel, payment_info: Dict[str, Any]) -> CfsAccountModel: + """Update pad account.""" + if str(payment_info.get('bankInstitutionNumber')) != cfs_account.bank_number or \ + str(payment_info.get('bankTransitNumber')) != cfs_account.bank_branch_number or \ + str(payment_info.get('bankAccountNumber')) != cfs_account.bank_account_number: + # This means, the current cfs_account is for PAD, not EFT + # Make the current CFS Account as INACTIVE in DB + cfs_account.status = CfsAccountStatus.INACTIVE.value + cfs_account.flush() + return cfs_account + + def create_invoice(self, payment_account: PaymentAccount, line_items: List[PaymentLineItem], invoice: Invoice, + **kwargs) -> None: + """Do nothing here, we create invoice references on the create CFS_INVOICES job.""" def apply_credit(self, invoice: Invoice, @@ -59,15 +84,13 @@ def apply_credit(self, payment_date=payment_date, paid_amount=invoice_balance - new_invoice_balance) - invoice_ref = self.create_invoice_reference(invoice=invoice_model, payment=payment) receipt = self.create_receipt(invoice=invoice_model, payment=payment) if auto_save: payment.save() - invoice_ref.save() receipt.save() - return payment, invoice_ref, receipt + return payment, receipt def complete_post_invoice(self, invoice: Invoice, invoice_reference: InvoiceReference) -> None: """Complete any post invoice activities if needed.""" @@ -89,17 +112,17 @@ def create_payment(self, payment_account: PaymentAccountModel, invoice: InvoiceM return payment @staticmethod - def create_invoice_reference(invoice: InvoiceModel, payment: PaymentModel) -> InvoiceReferenceModel: + def create_invoice_reference(invoice: InvoiceModel, invoice_number: str, + reference_number: str) -> InvoiceReferenceModel: """Create an invoice reference record.""" if not (invoice_reference := InvoiceReferenceModel - .find_any_active_reference_by_invoice_number(payment.invoice_number)): + .find_any_active_reference_by_invoice_number(invoice_number)): invoice_reference = InvoiceReferenceModel() invoice_reference.invoice_id = invoice.id - invoice_reference.invoice_number = payment.invoice_number - invoice_reference.status_code = InvoiceReferenceStatus.COMPLETED.value \ - if invoice.invoice_status_code == InvoiceStatus.PAID.value \ - else InvoiceReferenceStatus.ACTIVE.value + invoice_reference.invoice_number = invoice_number + invoice_reference.reference_number = reference_number + invoice_reference.status_code = InvoiceReferenceStatus.ACTIVE.value return invoice_reference diff --git a/pay-api/src/pay_api/services/eft_short_name_summaries.py b/pay-api/src/pay_api/services/eft_short_name_summaries.py new file mode 100644 index 000000000..82617c824 --- /dev/null +++ b/pay-api/src/pay_api/services/eft_short_name_summaries.py @@ -0,0 +1,141 @@ +# Copyright © 2024 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Service to provide summarized information for EFT Short names.""" +from __future__ import annotations + +from flask import current_app +from sqlalchemy import and_, func, or_ + +from pay_api.models import EFTCredit as EFTCreditModel +from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceModel +from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel +from pay_api.models import EFTShortnames as EFTShortnameModel +from pay_api.models import EFTShortnameSummarySchema as EFTSummarySchema +from pay_api.models import EFTTransaction as EFTTransactionModel +from pay_api.models import db +from pay_api.services.eft_short_names import EFTShortnamesSearch +from pay_api.utils.enums import EFTCreditInvoiceStatus, EFTFileLineType, EFTProcessStatus, EFTShortnameStatus +from pay_api.utils.util import unstructure_schema_items + + +class EFTShortnameSummaries: + """Service to provide summarized information for EFT Short names.""" + + @classmethod + def search(cls, search_criteria: EFTShortnamesSearch): + """Search eft short name summaries.""" + current_app.logger.debug('search') + return { + 'state_total': search_count, + 'page': search_criteria.page, + 'limit': search_criteria.limit, + 'items': summary_list, + 'total': pagination.total + } + + @staticmethod + def get_last_payment_received_query(): + """Query for EFT most recent transaction deposit date.""" + return db.session.query( + EFTTransactionModel.deposit_date, + EFTTransactionModel.short_name_id, + func.row_number().over(partition_by=EFTTransactionModel.short_name_id, + order_by=[EFTTransactionModel.deposit_date.desc(), + EFTTransactionModel.id]).label('rn') + ).filter(and_(EFTTransactionModel.short_name_id.isnot(None), + EFTTransactionModel.status_code == EFTProcessStatus.COMPLETED.value)) \ + .filter(EFTTransactionModel.line_type == EFTFileLineType.TRANSACTION.value) + + @staticmethod + def get_linked_count_query(): + """Query for EFT linked account count.""" + # pylint: disable=not-callable + return (db.session.query(EFTShortnameLinksModel.eft_short_name_id, + func.count( + EFTShortnameLinksModel.eft_short_name_id + ).label('count')) + .filter(EFTShortnameLinksModel.status_code.in_([EFTShortnameStatus.PENDING.value, + EFTShortnameStatus.LINKED.value])) + .group_by(EFTShortnameLinksModel.eft_short_name_id)) + + @staticmethod + def get_remaining_credit_query(): + """Query for EFT remaining credit amount.""" + return (db.session.query(EFTCreditModel.short_name_id, + (func.coalesce( + func.sum(EFTCreditModel.remaining_amount), 0) - func.coalesce( + func.sum(EFTCreditInvoiceModel.amount), 0)) + .label('total')) + .outerjoin(EFTCreditInvoiceModel, + and_(EFTCreditInvoiceModel.eft_credit_id == EFTCreditModel.id, + EFTCreditInvoiceModel.status_code == EFTCreditInvoiceStatus.PENDING.value)) + .group_by(EFTCreditModel.short_name_id)) + + @staticmethod + def get_search_count(): + """Get a total count of short name summary results.""" + current_app.logger.debug('get_search_count') + return count_query.count() + + @classmethod + def get_search_query(cls, search_criteria: EFTShortnamesSearch): + """Query for short names based on search criteria.""" + linked_account_subquery = cls.get_linked_count_query().subquery() + credit_remaining_subquery = cls.get_remaining_credit_query().subquery() + last_payment_subquery = cls.get_last_payment_received_query().subquery() + + query = (db.session.query( + EFTShortnameModel.id, + EFTShortnameModel.short_name, + func.coalesce(linked_account_subquery.c.count, 0).label('linked_accounts_count'), + func.coalesce(credit_remaining_subquery.c.total, 0).label('credits_remaining'), + last_payment_subquery.c.deposit_date.label('last_payment_received_date') + ).outerjoin( + linked_account_subquery, + linked_account_subquery.c.eft_short_name_id == EFTShortnameModel.id + ).outerjoin( + credit_remaining_subquery, + credit_remaining_subquery.c.short_name_id == EFTShortnameModel.id + ).outerjoin( + last_payment_subquery, + and_(last_payment_subquery.c.short_name_id == EFTShortnameModel.id, last_payment_subquery.c.rn == 1) + )) + + query = query.filter_conditionally(search_criteria.id, EFTShortnameModel.id) + query = query.filter_conditionally(search_criteria.short_name, EFTShortnameModel.short_name, is_like=True) + query = query.filter_conditional_date_range(start_date=search_criteria.deposit_start_date, + end_date=search_criteria.deposit_end_date, + model_attribute=last_payment_subquery.c.deposit_date) + query = query.filter_conditionally(search_criteria.credit_remaining, credit_remaining_subquery.c.total) + + if search_criteria.linked_accounts_count == 0: + query = query.filter(or_(linked_account_subquery.c.count == 0, linked_account_subquery.c.count.is_(None))) + else: + query = query.filter_conditionally(search_criteria.linked_accounts_count, linked_account_subquery.c.count) + + query = query.order_by(last_payment_subquery.c.deposit_date.asc()) + return query diff --git a/pay-api/src/pay_api/services/eft_short_names.py b/pay-api/src/pay_api/services/eft_short_names.py index 6316f8d6e..ced443315 100644 --- a/pay-api/src/pay_api/services/eft_short_names.py +++ b/pay-api/src/pay_api/services/eft_short_names.py @@ -17,25 +17,31 @@ from dataclasses import dataclass from datetime import date, datetime from operator import and_ -from typing import Any, Dict, List, Optional +from typing import List, Optional from _decimal import Decimal from flask import current_app -from sqlalchemy import case, func +from sqlalchemy import case, func, or_ +from sqlalchemy.sql.expression import exists from pay_api.exceptions import BusinessException from pay_api.factory.payment_system_factory import PaymentSystemFactory -from pay_api.models import EFTCredit as EFTCreditModel +from pay_api.models import CfsAccount as CfsAccountModel +from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceLinkModel +from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel +from pay_api.models import EFTShortnameLinkSchema from pay_api.models import EFTShortnames as EFTShortnameModel from pay_api.models import EFTShortnameSchema -from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel +from pay_api.models import Statement as StatementModel +from pay_api.models import StatementInvoices as StatementInvoicesModel from pay_api.models import db from pay_api.utils.converter import Converter -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameState, InvoiceStatus, PaymentMethod +from pay_api.utils.enums import EFTCreditInvoiceStatus, EFTShortnameStatus, InvoiceStatus, PaymentMethod from pay_api.utils.errors import Error from pay_api.utils.user_context import user_context +from pay_api.utils.util import unstructure_schema_items @dataclass @@ -43,17 +49,18 @@ class EFTShortnamesSearch: # pylint: disable=too-many-instance-attributes """Used for searching EFT short name records.""" id: Optional[int] = None - account_id_list: Optional[List[str]] = None account_id: Optional[str] = None + allow_partial_account_id: Optional[bool] = True account_name: Optional[str] = None account_branch: Optional[str] = None - transaction_start_date: Optional[date] = None - transaction_end_date: Optional[date] = None + amount_owing: Optional[Decimal] = None deposit_start_date: Optional[date] = None deposit_end_date: Optional[date] = None - deposit_amount: Optional[Decimal] = None + credit_remaining: Optional[Decimal] = None + linked_accounts_count: Optional[int] = None short_name: Optional[str] = None - state: Optional[str] = None + statement_id: Optional[int] = None + state: Optional[List[str]] = None page: Optional[int] = 1 limit: Optional[int] = 10 @@ -61,168 +68,86 @@ class EFTShortnamesSearch: # pylint: disable=too-many-instance-attributes class EFTShortnames: # pylint: disable=too-many-instance-attributes """Service to manage EFT short name model operations.""" - def __init__(self): - """Initialize service.""" - self.__dao = None - self._id: Optional[int] = None - self._auth_account_id: Optional[str] = None - self._short_name: Optional[str] = None - self._linked_by: Optional[str] = None - self._linked_by_name: Optional[str] = None - self._linked_on: Optional[datetime] = None - - @property - def _dao(self): - if not self.__dao: - self.__dao = EFTShortnameModel() - return self.__dao - - @_dao.setter - def _dao(self, value: EFTShortnameModel): - self.__dao = value - self.id: int = self._dao.id - self.auth_account_id: str = self._dao.auth_account_id - self.short_name: str = self._dao.short_name - self.linked_by: str = self._dao.linked_by - self.linked_by_name: str = self._dao.linked_by_name - self.linked_on: datetime = self._dao.linked_on - - @property - def id(self): - """Return the _id.""" - return self._id - - @id.setter - def id(self, value: int): - """Set the id.""" - self._id = value - self._dao.id = value - - @property - def auth_account_id(self): - """Return the auth_account_id.""" - return self._auth_account_id - - @auth_account_id.setter - def auth_account_id(self, value: str): - """Set the auth_account_id.""" - self._auth_account_id = value - self._dao.auth_account_id = value - - @property - def short_name(self): - """Return the short name.""" - return self._short_name - - @short_name.setter - def short_name(self, value: str): - """Set the short name.""" - self._short_name = value - self._dao.short_name = value - - @property - def created_on(self): - """Return the created_on date.""" - return self._created_on - - @created_on.setter - def created_on(self, value: datetime): - """Set the created on date.""" - self._created_on = value - self._dao.created_on = value - - @property - def linked_by(self): - """Return the linked by user name.""" - return self._linked_by - - @linked_by.setter - def linked_by(self, value: str): - """Set the linked by user name.""" - self._linked_by = value - self._dao.linked_by = value - - @property - def linked_by_name(self): - """Return the linked by name.""" - return self._linked_by - - @linked_by_name.setter - def linked_by_name(self, value: str): - """Set the linked by name.""" - self._linked_by_name = value - self._dao.linked_by_name = value - - @property - def linked_on(self): - """Return the linked on date.""" - return self._linked_on - - @linked_on.setter - def linked_on(self, value: str): - """Set the linked on date.""" - self._linked_on = value - self._dao.linked_on = value - - def save(self): - """Save the information to the DB.""" - return self._dao.save() - - def flush(self): - """Flush the information to the DB.""" - return self._dao.flush() - - @classmethod - def _save(cls, short_name_request: Dict[str, any], short_name: EFTShortnameModel): - """Update and save eft short name model.""" - short_name.short_name = short_name_request.get('shortName') - short_name.auth_account_id = short_name_request.get('accountId', None) - short_name.flush() - short_name.save() - - @classmethod - def update(cls, short_name_id: str, short_name_request: Dict[str, Any]) -> EFTShortnames: - """Create or update payment account record.""" - current_app.logger.debug('update short name mapping') - return cls.find_by_short_name_id(short_name.id) - @classmethod @user_context - def patch(cls, short_name_id: int, auth_account_id: str, **kwargs) -> EFTShortnames: - """Patch eft short name auth account mapping.""" - current_app.logger.debug(' EFTShortnameLinksModel: + """Create EFT short name auth account link.""" + current_app.logger.debug('create_shortname_link') + return cls.find_link_by_id(eft_short_name_link.id) - # Process any invoices owing for short name mapping - cls.process_owing_invoices(short_name_id) + @classmethod + def delete_shortname_link(cls, short_name_link_id: int): + """Delete EFT short name auth account link.""" + current_app.logger.debug('patch short name mapping') - return cls.find_by_short_name_id(short_name.id) + if short_name_link.status_code != EFTShortnameStatus.PENDING.value: + raise BusinessException(Error.EFT_SHORT_NAME_LINK_INVALID_STATUS) + + short_name_link.delete() + current_app.logger.debug('>delete_shortname_link') + + @classmethod + def get_shortname_links(cls, short_name_id: int) -> dict: + """Get EFT short name account links.""" + current_app.logger.debug('get_shortname_links') + return { + 'items': link_list + } @staticmethod def process_owing_invoices(short_name_id: int) -> EFTShortnames: @@ -256,15 +181,53 @@ def get_invoices_owing(auth_account_id: str) -> [InvoiceModel]: @classmethod def find_by_short_name_id(cls, short_name_id: int) -> EFTShortnames: - """Find payment account by corp number, corp type and payment system code.""" + """Find EFT short name by short name id.""" current_app.logger.debug('find_by_short_name_id') return result + @classmethod + def find_by_auth_account_id(cls, auth_account_id: str) -> List[EFTShortnames]: + """Find EFT shortname by auth account id.""" + current_app.logger.debug('find_by_auth_account_id') + return result + + @classmethod + def find_by_auth_account_id_state(cls, auth_account_id: str, state: List[str]) -> List[EFTShortnames]: + """Find EFT shortname by auth account id that are linked.""" + current_app.logger.debug('find_by_auth_account_id_state') + return result + + @classmethod + def find_link_by_id(cls, link_id: int) -> List[EFTShortnames]: + """Find EFT shortname link by id.""" + current_app.logger.debug('find_link_by_id') + return result + @classmethod def search(cls, search_criteria: EFTShortnamesSearch): """Search eft short name records.""" @@ -274,9 +237,7 @@ def search(cls, search_criteria: EFTShortnamesSearch): pagination = search_query.paginate(per_page=search_criteria.limit, page=search_criteria.page) - short_name_list = [EFTShortnameSchema.from_row(short_name) for short_name in pagination.items] - converter = Converter() - short_name_list = converter.unstructure(short_name_list) + short_name_list = unstructure_schema_items(EFTShortnameSchema, pagination.items) current_app.logger.debug('>search') return { @@ -287,6 +248,21 @@ def search(cls, search_criteria: EFTShortnamesSearch): 'total': pagination.total } + @staticmethod + def get_statement_summary_query(): + """Query for latest statement id and total amount owing of invoices in statements.""" + return db.session.query( + StatementModel.payment_account_id, + func.max(StatementModel.id).label('latest_statement_id'), + func.coalesce(func.sum(InvoiceModel.total - InvoiceModel.paid), 0).label('total_owing') + ).outerjoin( + StatementInvoicesModel, + StatementInvoicesModel.statement_id == StatementModel.id + ).outerjoin( + InvoiceModel, + InvoiceModel.id == StatementInvoicesModel.invoice_id + ).group_by(StatementModel.payment_account_id) + @classmethod def get_search_count(cls, search_criteria: EFTShortnamesSearch): """Get total count of results based on short name state search criteria.""" @@ -299,100 +275,108 @@ def get_search_count(cls, search_criteria: EFTShortnamesSearch): return count_query.count() @staticmethod - def get_ordered_transaction_query(): - """Query for EFT transactions.""" - return db.session.query( - EFTTransactionModel.id, - EFTTransactionModel.transaction_date, - EFTTransactionModel.deposit_date, - EFTTransactionModel.deposit_amount_cents, - EFTTransactionModel.short_name_id, - func.row_number().over(partition_by=EFTTransactionModel.short_name_id, - order_by=[EFTTransactionModel.transaction_date, EFTTransactionModel.id]).label('rn') - ).filter(and_(EFTTransactionModel.short_name_id.isnot(None), - EFTTransactionModel.status_code == EFTProcessStatus.COMPLETED.value))\ - .filter(EFTTransactionModel.line_type == EFTFileLineType.TRANSACTION.value) + def add_payment_account_name_columns(query): + """Add payment account name and branch to query select columns.""" + return query.add_columns(case( + (PaymentAccountModel.name.like('%-' + PaymentAccountModel.branch_name), + func.replace(PaymentAccountModel.name, '-' + PaymentAccountModel.branch_name, '') + ), else_=PaymentAccountModel.name).label('account_name'), + PaymentAccountModel.branch_name.label('account_branch')) + + @staticmethod + def get_pending_payment_count(): + """Get count of pending EFT Credit Invoice Links.""" + return (db.session.query(db.func.count(InvoiceModel.id).label('invoice_count')) + .join(EFTCreditInvoiceLinkModel, EFTCreditInvoiceLinkModel.invoice_id == InvoiceModel.id) + .filter(InvoiceModel.payment_account_id == PaymentAccountModel.id) + .filter(EFTCreditInvoiceLinkModel.status_code.in_([EFTCreditInvoiceStatus.PENDING.value])) + .correlate(PaymentAccountModel) + .as_scalar()) @classmethod def get_search_query(cls, search_criteria: EFTShortnamesSearch, is_count: bool = False): """Query for short names based on search criteria.""" - sub_query = None + statement_summary_query = cls.get_statement_summary_query().subquery() # Case statement is to check for and remove the branch name from the name, so they can be filtered on separately # The branch name was added to facilitate a better short name search experience and the existing # name is preserved as it was with '-' concatenated with the branch name for reporting purposes - query = db.session.query(EFTShortnameModel.id, - EFTShortnameModel.short_name, - EFTShortnameModel.auth_account_id, - EFTShortnameModel.created_on, - EFTShortnameModel.linked_by, - EFTShortnameModel.linked_by_name, - EFTShortnameModel.linked_on, - case( - [(PaymentAccountModel.name.like('%-' + PaymentAccountModel.branch_name), - func.replace(PaymentAccountModel.name, '-' + PaymentAccountModel.branch_name, '') - )], - else_=PaymentAccountModel.name - ).label('account_name'), - PaymentAccountModel.branch_name.label('account_branch')) + query = (db.session.query(EFTShortnameModel.id, + EFTShortnameModel.short_name, + EFTShortnameModel.created_on, + EFTShortnameLinksModel.status_code, + EFTShortnameLinksModel.auth_account_id, + case( + (EFTShortnameLinksModel.auth_account_id.is_(None), + EFTShortnameStatus.UNLINKED.value + ), + else_=EFTShortnameLinksModel.status_code + ).label('status_code'), + CfsAccountModel.status.label('cfs_account_status')) + .outerjoin(EFTShortnameLinksModel, EFTShortnameLinksModel.eft_short_name_id == EFTShortnameModel.id) + .outerjoin(PaymentAccountModel, + PaymentAccountModel.auth_account_id == EFTShortnameLinksModel.auth_account_id) + .outerjoin(CfsAccountModel, + CfsAccountModel.account_id == PaymentAccountModel.id)) # Join payment information if this is NOT the count query if not is_count: - sub_query = cls.get_ordered_transaction_query().subquery() - query = query.add_columns(sub_query.c.id.label('transaction_id'), - sub_query.c.deposit_date.label('deposit_date'), - sub_query.c.transaction_date.label('transaction_date'), - sub_query.c.deposit_amount_cents.label('deposit_amount')) \ - .outerjoin(sub_query, and_(sub_query.c.short_name_id == EFTShortnameModel.id, sub_query.c.rn == 1)) \ - .outerjoin(PaymentAccountModel, - PaymentAccountModel.auth_account_id == EFTShortnameModel.auth_account_id) - - # Sub query filters for EFT dates - query = query.filter_conditional_date_range(start_date=search_criteria.transaction_start_date, - end_date=search_criteria.transaction_end_date, - model_attribute=sub_query.c.transaction_date) - - query = query.filter_conditional_date_range(start_date=search_criteria.deposit_start_date, - end_date=search_criteria.deposit_end_date, - model_attribute=sub_query.c.deposit_date) - - # Sub query filters - query = query.filter_conditionally(search_criteria.deposit_amount, sub_query.c.deposit_amount_cents) + query = cls.add_payment_account_name_columns(query) + query = (query.add_columns( + statement_summary_query.c.total_owing, + statement_summary_query.c.latest_statement_id + ).outerjoin( + statement_summary_query, + statement_summary_query.c.payment_account_id == PaymentAccountModel.id + )) + + # Short name link filters + query = query.filter_conditionally(search_criteria.id, EFTShortnameModel.id) query = query.filter_conditionally(search_criteria.account_id, - EFTShortnameModel.auth_account_id, is_like=True) + EFTShortnameLinksModel.auth_account_id, + is_like=True) # Payment account filters query = query.filter_conditionally(search_criteria.account_name, PaymentAccountModel.name, is_like=True) query = query.filter_conditionally(search_criteria.account_branch, PaymentAccountModel.branch_name, is_like=True) - # Filter by short name state - if search_criteria.state == EFTShortnameState.UNLINKED.value: - query = query.filter(EFTShortnameModel.auth_account_id.is_(None)) - elif search_criteria.state == EFTShortnameState.LINKED.value: - query = query.filter(EFTShortnameModel.auth_account_id.isnot(None)) + # Statement summary filters + query = query.filter_conditionally(search_criteria.statement_id, + statement_summary_query.c.latest_statement_id) + if search_criteria.amount_owing == 0: + query = query.filter(or_(statement_summary_query.c.total_owing == 0, + statement_summary_query.c.total_owing.is_(None))) + else: + query = query.filter_conditionally(search_criteria.amount_owing, statement_summary_query.c.total_owing) - # Filter by a list of auth account ids - full match - if search_criteria.account_id_list: - query = query.filter(EFTShortnameModel.auth_account_id.in_(search_criteria.account_id_list)) + query = cls.get_link_state_filters(search_criteria, query) + query = query.filter( + or_(PaymentAccountModel.payment_method == PaymentMethod.EFT.value, PaymentAccountModel.id.is_(None)) + ) # Short name filters query = query.filter_conditionally(search_criteria.id, EFTShortnameModel.id) query = query.filter_conditionally(search_criteria.short_name, EFTShortnameModel.short_name, is_like=True) - query = cls.get_order_by(search_criteria, query, sub_query) - + if not is_count: + query = query.order_by(EFTShortnameModel.short_name.asc(), EFTShortnameLinksModel.auth_account_id.asc()) return query @classmethod - def get_order_by(cls, search_criteria, query, sub_query): - """Get the order by for search query.""" - if search_criteria.state == EFTShortnameState.LINKED.value: - return query.order_by(EFTShortnameModel.linked_on.desc()) - - if search_criteria.state == EFTShortnameState.UNLINKED.value and sub_query is not None: - return query.order_by(sub_query.c.transaction_date.desc()) - + def get_link_state_filters(cls, search_criteria, query): + """Build filters for link states.""" + if search_criteria.state: + if EFTShortnameStatus.UNLINKED.value in search_criteria.state: + # There can be multiple links to a short name, look for any links that don't have an UNLINKED status + # if they don't exist return the short name. + query = query.filter( + ~exists() + .where(EFTShortnameLinksModel.status_code != EFTShortnameStatus.UNLINKED.value) + .where(EFTShortnameLinksModel.eft_short_name_id == EFTShortnameModel.id) + .correlate(EFTShortnameModel) + ) + if EFTShortnameStatus.LINKED.value in search_criteria.state: + query = query.filter( + EFTShortnameLinksModel.status_code.in_([EFTShortnameStatus.PENDING.value, + EFTShortnameStatus.LINKED.value]) + ) return query - - def asdict(self): - """Return the EFT Short name as a python dict.""" - return Converter().unstructure(EFTShortnameSchema.from_row(self._dao)) diff --git a/pay-api/src/pay_api/services/eft_transactions.py b/pay-api/src/pay_api/services/eft_transactions.py index 5dddacc47..99eda5c16 100644 --- a/pay-api/src/pay_api/services/eft_transactions.py +++ b/pay-api/src/pay_api/services/eft_transactions.py @@ -15,13 +15,19 @@ from dataclasses import dataclass from typing import Optional -from sqlalchemy import func +from sqlalchemy import Float, and_, case, cast, desc, func, literal, null from pay_api.models import EFTCredit as EFTCreditModel +from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceLinkModel from pay_api.models import EFTTransaction as EFTTransactionModel -from pay_api.models import EFTTransactionSchema, db -from pay_api.utils.converter import Converter -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus +from pay_api.models import EFTTransactionSchema +from pay_api.models import Invoice as InvoiceModel +from pay_api.models import PaymentAccount as PaymentAccountModel +from pay_api.models import Statement as StatementModel +from pay_api.models import StatementInvoices as StatementInvoicesModel +from pay_api.models import db +from pay_api.utils.enums import EFTCreditInvoiceStatus, EFTFileLineType, EFTProcessStatus +from pay_api.utils.util import unstructure_schema_items @dataclass @@ -36,36 +42,84 @@ class EFTTransactions: """Service to manage EFT Transactions.""" @staticmethod - def get_remaining_credits(short_name_id: int): - """Return the remaining credit for a short name.""" - return db.session.query(func.sum(EFTCreditModel.remaining_amount))\ - .filter(EFTCreditModel.short_name_id == short_name_id)\ - .group_by(EFTCreditModel.short_name_id).scalar() + def get_account_name(): + """Return case statement for deriving payment account name.""" + return case( + (PaymentAccountModel.name.like('%-' + PaymentAccountModel.branch_name), + func.replace(PaymentAccountModel.name, '-' + PaymentAccountModel.branch_name, '') + ), else_=PaymentAccountModel.name).label('account_name') + + @staticmethod + def get_funds_received_query(): + """Return the EFT transaction funds received.""" + # Null valued columns are defined for the purposes of unioning with funds applied query + # We don't need the account information and there will be no statement for EFT Transactions received + # through a TDI17 + return db.session.query(EFTTransactionModel.id.label('transaction_id'), + EFTTransactionModel.short_name_id, + EFTTransactionModel.deposit_date.label('transaction_date'), + (cast( + EFTTransactionModel.deposit_amount_cents, Float) / 100 + ).label('transaction_amount'), + literal('Funds Received').label('transaction_description'), + null().label('statement_id'), + null().label('auth_account_id'), + null().label('account_name'), + null().label('account_branch')) + + @staticmethod + def get_funds_applied_query(): + """Return the EFT transaction funds applied by account statement.""" + return (db.session.query(StatementModel.payment_account_id, + EFTCreditModel.short_name_id, + func.max(InvoiceModel.payment_date).label('transaction_date'), + StatementInvoicesModel.statement_id, + func.sum(EFTCreditInvoiceLinkModel.amount).label('paid_amount')) + .join(InvoiceModel, InvoiceModel.id == StatementInvoicesModel.invoice_id) + .join(StatementModel, StatementModel.id == StatementInvoicesModel.statement_id) + .join(EFTCreditInvoiceLinkModel, + and_(EFTCreditInvoiceLinkModel.invoice_id == StatementInvoicesModel.invoice_id, + EFTCreditInvoiceLinkModel.status_code == EFTCreditInvoiceStatus.COMPLETED.value)) + .join(EFTCreditModel, EFTCreditModel.id == EFTCreditInvoiceLinkModel.eft_credit_id) + .group_by(StatementModel.payment_account_id, + EFTCreditModel.short_name_id, + StatementInvoicesModel.statement_id) + ) @classmethod def search(cls, short_name_id: int, search_criteria: EFTTransactionSearch = EFTTransactionSearch()) -> [EFTTransactionModel]: """Return EFT Transfers by search criteria.""" - query = db.session.query(EFTTransactionModel) \ - .filter(EFTTransactionModel.short_name_id == short_name_id) \ - .filter(EFTTransactionModel.status_code == EFTProcessStatus.COMPLETED.value) \ - .filter(EFTTransactionModel.line_type == EFTFileLineType.TRANSACTION.value) \ - .order_by(EFTTransactionModel.transaction_date.desc()) + funds_received_query = (cls.get_funds_received_query() + .filter(EFTTransactionModel.short_name_id == short_name_id) + .filter(EFTTransactionModel.status_code == EFTProcessStatus.COMPLETED.value) + .filter(EFTTransactionModel.line_type == EFTFileLineType.TRANSACTION.value)) + + funds_applied_subquery = cls.get_funds_applied_query().subquery() + funds_applied_query = (db.session.query(null().label('transaction_id'), + funds_applied_subquery.c.short_name_id, + funds_applied_subquery.c.transaction_date, + funds_applied_subquery.c.paid_amount.label('transaction_amount'), + literal('Statement Paid').label('transaction_description'), + funds_applied_subquery.c.statement_id, + PaymentAccountModel.auth_account_id, + cls.get_account_name(), + PaymentAccountModel.branch_name) + .join(funds_applied_subquery, + and_(funds_applied_subquery.c.payment_account_id == PaymentAccountModel.id, + funds_applied_subquery.c.short_name_id == short_name_id))) - pagination = query.paginate(per_page=search_criteria.limit, - page=search_criteria.page) + union_query = funds_received_query.union(funds_applied_query) + union_query = union_query.order_by(desc('transaction_date')) - transaction_list = [EFTTransactionSchema.from_row(transaction) for transaction in pagination.items] - converter = Converter() - transaction_list = converter.unstructure(transaction_list) + pagination = union_query.paginate(per_page=search_criteria.limit, + page=search_criteria.page) - remaining_credit = cls.get_remaining_credits(short_name_id) - remaining_credit = float(remaining_credit) if remaining_credit else 0 + transaction_list = unstructure_schema_items(EFTTransactionSchema, pagination.items) return { 'page': search_criteria.page, 'limit': search_criteria.limit, 'items': transaction_list, - 'total': pagination.total, - 'remaining_credit': remaining_credit + 'total': pagination.total } diff --git a/pay-api/src/pay_api/services/fas/routing_slip.py b/pay-api/src/pay_api/services/fas/routing_slip.py index 094de902a..aed8b6e12 100644 --- a/pay-api/src/pay_api/services/fas/routing_slip.py +++ b/pay-api/src/pay_api/services/fas/routing_slip.py @@ -219,12 +219,10 @@ def create_daily_reports(cls, date: str, **kwargs): total_cheque_usd = 0 total_cash_cad = 0 total_cheque_cad = 0 - # TODO Only CAD supported now, so just add up the total. for routing_slip in routing_slips: total += float(routing_slip.total) if routing_slip.payment_account.payment_method == PaymentMethod.CASH.value: no_of_cash += 1 - # TODO check if the payment is CAD or USD. total_cash_cad += routing_slip.total if routing_slip.total_usd is not None: total_cash_usd += routing_slip.total_usd diff --git a/pay-api/src/pay_api/services/fee_schedule.py b/pay-api/src/pay_api/services/fee_schedule.py index dcca193f4..354696b06 100644 --- a/pay-api/src/pay_api/services/fee_schedule.py +++ b/pay-api/src/pay_api/services/fee_schedule.py @@ -211,12 +211,12 @@ def service_fees(self, value: Decimal): @property def gst(self): """Return the fee amount.""" - return 0 # TODO + return 0 @property def pst(self): """Return the fee amount.""" - return 0 # TODO + return 0 @property def quantity(self): @@ -338,7 +338,7 @@ def find_all( } fee_schdules = FeeScheduleModel.find_all(corp_type_code=corp_type, filing_type_code=filing_type_code, description=description) - schdule_schema = FeeScheduleSchema(exclude=('distribution_codes',)) + schdule_schema = FeeScheduleSchema() data['items'] = schdule_schema.dump(fee_schdules, many=True) current_app.logger.debug('>find_all') return data @@ -353,7 +353,6 @@ def calculate_service_fees(fee_schedule_model: FeeScheduleModel, account_fee: Ac service_fees: float = 0 # TODO for system accounts with role EXCLUDE_SERVICE_FEES, do not charge service fees for now. - # Handle it properly later if not user.is_staff() and \ not (user.is_system() and Role.EXCLUDE_SERVICE_FEES.value in user.roles) \ and fee_schedule_model.fee.amount > 0 and fee_schedule_model.service_fee: diff --git a/pay-api/src/pay_api/services/gcp_queue/logging.py b/pay-api/src/pay_api/services/gcp_queue/logging.py new file mode 100644 index 000000000..938857455 --- /dev/null +++ b/pay-api/src/pay_api/services/gcp_queue/logging.py @@ -0,0 +1,67 @@ +# Copyright © 2023 Province of British Columbia +# +# Licensed under the BSD 3 Clause License, (the "License"); +# you may not use this file except in compliance with the License. +# The template for the license can be found here +# https://opensource.org/license/bsd-3-clause/ +# +# Redistribution and use in source and binary forms, +# with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +"""Structured logging based on emitting to knative.""" +import inspect +import json +import os + +from werkzeug.local import LocalProxy + + +def structured_log(request: LocalProxy, severity: str = 'NOTICE', message: str = None): + """Print structured log message.""" + frm = inspect.stack()[1] + mod = inspect.getmodule(frm[0]) + + # Build structured log messages as an object. + global_log_fields = {} + + if project := os.environ.get('GOOGLE_CLOUD_PROJECT'): + # Add log correlation to nest all log messages. + trace_header = request.headers.get('X-Cloud-Trace-Context') + + if trace_header and project: + trace = trace_header.split('/') + global_log_fields['logging.googleapis.com/trace'] = f'projects/{project}/traces/{trace[0]}' + + # Complete a structured log entry. + entry = { + 'severity': severity, + 'message': message, + # Log viewer accesses 'component' as jsonPayload.component'. + 'component': f'{mod.__name__}.{frm.function}', + **global_log_fields, + } + + print(json.dumps(entry)) diff --git a/pay-api/src/pay_api/services/internal_pay_service.py b/pay-api/src/pay_api/services/internal_pay_service.py index 4fbc6edb3..00b184e4d 100644 --- a/pay-api/src/pay_api/services/internal_pay_service.py +++ b/pay-api/src/pay_api/services/internal_pay_service.py @@ -58,8 +58,7 @@ def create_invoice(self, payment_account: PaymentAccount, line_items: List[Payme if not is_zero_dollar_invoice and routing_slip is not None: # creating invoice in cfs is done in job current_app.logger.info(f'FAS Routing slip found with remaining amount : {routing_slip.remaining_amount}') - routing_slip.remaining_amount = routing_slip.remaining_amount - \ - get_quantized(invoice.total) + routing_slip.remaining_amount -= get_quantized(invoice.total) if routing_slip.status == RoutingSlipStatus.ACTIVE.value and routing_slip.remaining_amount < 0.01: routing_slip.status = RoutingSlipStatus.COMPLETE.value routing_slip.flush() diff --git a/pay-api/src/pay_api/services/non_sufficient_funds.py b/pay-api/src/pay_api/services/non_sufficient_funds.py index 83eadf582..a96e25870 100644 --- a/pay-api/src/pay_api/services/non_sufficient_funds.py +++ b/pay-api/src/pay_api/services/non_sufficient_funds.py @@ -97,7 +97,7 @@ def query_all_non_sufficient_funds_invoices(account_id: str): InvoiceModel.id.label('invoice_id'), (InvoiceModel.total - InvoiceModel.paid).label('amount_remaining'), func.max(case( - [(PaymentLineItemModel.description == ReverseOperation.NSF.value, PaymentLineItemModel.total)], + (PaymentLineItemModel.description == ReverseOperation.NSF.value, PaymentLineItemModel.total), else_=0)).label('nsf_amount') ) .join(InvoiceReferenceModel, InvoiceReferenceModel.invoice_id == InvoiceModel.id) diff --git a/pay-api/src/pay_api/services/payment.py b/pay-api/src/pay_api/services/payment.py index 8fe1d8c02..5e6bc9aff 100644 --- a/pay-api/src/pay_api/services/payment.py +++ b/pay-api/src/pay_api/services/payment.py @@ -476,7 +476,7 @@ def _prepare_csv_data(results): ), total_fees, total_gst + total_pst, - total_fees - service_fee, # TODO + total_fees - service_fee, service_fee, invoice.get('status_code'), invoice.get('business_identifier'), diff --git a/pay-api/src/pay_api/services/payment_account.py b/pay-api/src/pay_api/services/payment_account.py index 72612fc77..d67bdca16 100644 --- a/pay-api/src/pay_api/services/payment_account.py +++ b/pay-api/src/pay_api/services/payment_account.py @@ -40,6 +40,7 @@ from pay_api.services import gcp_queue_publisher from pay_api.services.cfs_service import CFSService from pay_api.services.distribution_code import DistributionCode +from pay_api.services.gcp_queue_publisher import QueueMessage from pay_api.services.oauth_service import OAuthService from pay_api.services.receipt import Receipt as ReceiptService from pay_api.services.statement import Statement @@ -151,8 +152,9 @@ def auth_account_id(self): @auth_account_id.setter def auth_account_id(self, value: str): """Set the auth_account_id.""" - self._auth_account_id = value - self._dao.auth_account_id = value + if self._auth_account_id != value: + self._auth_account_id = value + self._dao.auth_account_id = value @property def name(self): @@ -162,8 +164,9 @@ def name(self): @name.setter def name(self, value: str): """Set the name.""" - self._name = value - self._dao.name = value + if self._name != value: + self._name = value + self._dao.name = value @property def payment_method(self): @@ -173,8 +176,9 @@ def payment_method(self): @payment_method.setter def payment_method(self, value: int): """Set the payment_method.""" - self._payment_method = value - self._dao.payment_method = value + if self._payment_method != value: + self._payment_method = value + self._dao.payment_method = value @property def cfs_account(self): @@ -321,8 +325,9 @@ def billable(self): @billable.setter def billable(self, value: bool): """Set the billable.""" - self._billable = value - self._dao.billable = value + if self._billable != value: + self._billable = value + self._dao.billable = value @property def eft_enable(self): @@ -332,8 +337,9 @@ def eft_enable(self): @eft_enable.setter def eft_enable(self, value: bool): """Set the eft_enable.""" - self._eft_enable = value - self._dao.eft_enable = value + if self._eft_enable != value: + self._eft_enable = value + self._dao.eft_enable = value def save(self): """Save the information to the DB.""" @@ -437,7 +443,7 @@ def _save_account(cls, account_request: Dict[str, any], payment_account: Payment # pylint:disable=cyclic-import, import-outside-toplevel from pay_api.factory.payment_system_factory import PaymentSystemFactory - payment_account.auth_account_id = account_request.get('accountId') + payment_account.auth_account_id = str(account_request.get('accountId')) # If the payment method is CC, set the payment_method as DIRECT_PAY if payment_method := get_str_by_path(account_request, 'paymentInfo/methodOfPayment'): @@ -470,6 +476,7 @@ def _save_account(cls, account_request: Dict[str, any], payment_account: Payment # 2. Existing payment account: # - If the account was on DIRECT_PAY and switching to Online Banking, and active CFS account is not present. # - If the account was on DRAWDOWN and switching to PAD, and active CFS account is not present + # - If the account was on PAD and switching to EFT, and active CFS account is not present if payment_method: pay_system = PaymentSystemFactory.create_from_payment_method(payment_method=payment_method) @@ -484,7 +491,10 @@ def _handle_payment_details(cls, account_request, is_sandbox, pay_system, paymen cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(payment_account.id) \ if payment_account.id else None if pay_system.get_payment_system_code() == PaymentSystem.PAYBC.value: - if cfs_account is None: + if cfs_account is None or (payment_account.payment_method == PaymentMethod.EFT and cfs_account): + if payment_account.payment_method == PaymentMethod.EFT and cfs_account: + pay_system.update_account(name=payment_account.name, cfs_account=cfs_account, + payment_info=payment_info) cfs_account = pay_system.create_account( # pylint:disable=assignment-from-none identifier=payment_account.auth_account_id, contact_info=account_request.get('contactInfo'), @@ -607,7 +617,7 @@ def _get_payment_based_on_pad_activation(account: PaymentAccountModel) -> Tuple[ else: # Handle repeated changing of pad to bcol ;then to pad again etc new_activation_date = account.pad_activation_date # was already in pad ;no need to extend - is_previous_pad_activated = account.pad_activation_date < datetime.now() + is_previous_pad_activated = new_activation_date < datetime.now(new_activation_date.tzinfo) if is_previous_pad_activated: # was in PAD ; so no need of activation period wait time and no need to be in bcol..so use PAD again new_payment_method = PaymentMethod.PAD.value @@ -797,7 +807,8 @@ def asdict(self, **kwargs): def _is_pad_in_pending_activation(self): """Find if PAD is awaiting activation.""" - return self.pad_activation_date and self.pad_activation_date > datetime.now() and self.cfs_account_status in \ + return self.pad_activation_date and self.pad_activation_date > datetime.now(self.pad_activation_date.tzinfo) \ + and self.cfs_account_status in \ (CfsAccountStatus.PENDING.value, CfsAccountStatus.PENDING_PAD_ACTIVATION.value) def publish_account_mailer_event_on_creation(self): @@ -807,11 +818,11 @@ def publish_account_mailer_event_on_creation(self): include_pay_info=True) self._publish_queue_message(payload, QueueMessageTypes.PAD_ACCOUNT_CREATE.value) - def _publish_queue_message(self, payload, message_type): + def _publish_queue_message(self, payload: dict, message_type: str): """Publish to account mailer to send out confirmation email or notification email.""" try: gcp_queue_publisher.publish_to_queue( - gcp_queue_publisher.QueueMessage( + QueueMessage( source=QueueSources.PAY_API.value, message_type=message_type, payload=payload, @@ -873,7 +884,7 @@ def unlock_frozen_accounts(payment: Payment): try: gcp_queue_publisher.publish_to_queue( - gcp_queue_publisher.QueueMessage( + QueueMessage( source=QueueSources.PAY_API.value, message_type=QueueMessageTypes.NSF_UNLOCK_ACCOUNT.value, payload=payload, diff --git a/pay-api/src/pay_api/services/statement.py b/pay-api/src/pay_api/services/statement.py index 111932490..ab0f9e77f 100644 --- a/pay-api/src/pay_api/services/statement.py +++ b/pay-api/src/pay_api/services/statement.py @@ -210,8 +210,9 @@ def populate_overdue_from_invoices(statements: List[StatementModel]): """Populate is_overdue field for statements.""" # Invoice status can change after a statement has been generated. statement_ids = [statements.id for statements in statements] - overdue_statements = db.session.query(func.count(InvoiceModel.id).label('overdue_invoices'), - StatementInvoicesModel.statement_id) \ + overdue_statements = db.session.query( + func.count(InvoiceModel.id).label('overdue_invoices'), # pylint:disable=not-callable + StatementInvoicesModel.statement_id) \ .join(StatementInvoicesModel) \ .filter(InvoiceModel.invoice_status_code == InvoiceStatus.OVERDUE.value) \ .filter(StatementInvoicesModel.invoice_id == InvoiceModel.id) \ diff --git a/pay-api/src/pay_api/utils/constants.py b/pay-api/src/pay_api/utils/constants.py index ac82529dd..657e5793a 100644 --- a/pay-api/src/pay_api/utils/constants.py +++ b/pay-api/src/pay_api/utils/constants.py @@ -21,6 +21,7 @@ DEFAULT_CURRENCY = 'CAD' RECEIPT_METHOD_PAD_DAILY = 'BCR-PAD Daily' RECEIPT_METHOD_PAD_STOP = 'BCR-PAD Stop' +RECEIPT_METHOD_EFT_MONTHLY = 'BCR-EFT MONTHLY' CFS_BATCH_SOURCE = 'BC REG MANUAL_OTHER' CFS_CM_BATCH_SOURCE = 'MANUAL-OTHER' diff --git a/pay-api/src/pay_api/utils/enums.py b/pay-api/src/pay_api/utils/enums.py index b5d70e283..7a91fd76a 100644 --- a/pay-api/src/pay_api/utils/enums.py +++ b/pay-api/src/pay_api/utils/enums.py @@ -310,6 +310,15 @@ class CfsReceiptStatus(Enum): REV = 'REV' +class EFTCreditInvoiceStatus(Enum): + """EFT Credit Invoice Link Status.""" + + COMPLETED = 'COMPLETED' + PENDING = 'PENDING' + PENDING_REFUND = 'PENDING_REFUND' + REFUNDED = 'REFUNDED' + + class EFTProcessStatus(Enum): """EFT Process Status.""" @@ -335,11 +344,13 @@ class EFTGlTransferType(Enum): TRANSFER = 'TRANSFER' -class EFTShortnameState(Enum): - """EFT Short name search states.""" +class EFTShortnameStatus(Enum): + """EFT Short name statuses.""" + INACTIVE = 'INACTIVE' LINKED = 'LINKED' UNLINKED = 'UNLINKED' + PENDING = 'PENDING' class PaymentDetailsGlStatus(Enum): @@ -358,3 +369,10 @@ class QueueSources(Enum): PAY_JOBS = 'pay-jobs' PAY_QUEUE = 'pay-queue' FTP_POLLER = 'ftp-poller' + + +class EJVLinkType(Enum): + """EJV link types for ejv_link table.""" + + INVOICE = 'invoice' + REFUND = 'refund' diff --git a/pay-api/src/pay_api/utils/errors.py b/pay-api/src/pay_api/utils/errors.py index 30a064df8..2f7a747d4 100644 --- a/pay-api/src/pay_api/utils/errors.py +++ b/pay-api/src/pay_api/utils/errors.py @@ -72,6 +72,7 @@ class Error(Enum): EFT_SHORT_NAME_EXISTS = 'EFT_SHORT_NAME_EXISTS', HTTPStatus.BAD_REQUEST EFT_SHORT_NAME_ACCOUNT_ID_REQUIRED = 'EFT_SHORT_NAME_ACCOUNT_ID_REQUIRED', HTTPStatus.BAD_REQUEST EFT_SHORT_NAME_ALREADY_MAPPED = 'EFT_SHORT_NAME_ALREADY_MAPPED', HTTPStatus.BAD_REQUEST + EFT_SHORT_NAME_LINK_INVALID_STATUS = 'EFT_SHORT_NAME_LINK_INVALID_STATUS', HTTPStatus.BAD_REQUEST # FAS Errors FAS_INVALID_PAYMENT_METHOD = 'FAS_INVALID_PAYMENT_METHOD', HTTPStatus.BAD_REQUEST diff --git a/pay-api/src/pay_api/utils/logging.py b/pay-api/src/pay_api/utils/logging.py index 4bec17609..905300f5a 100755 --- a/pay-api/src/pay_api/utils/logging.py +++ b/pay-api/src/pay_api/utils/logging.py @@ -18,12 +18,7 @@ def setup_logging(conf): - """Create the services logger. - - TODO should be reworked to load in the proper loggers and remove others - """ - # log_file_path = path.join(path.abspath(path.dirname(__file__)), conf) - + """Create the services logger.""" if conf and path.isfile(conf): logging.config.fileConfig(conf) print(f'Configure logging, from conf:{conf}', file=sys.stdout) diff --git a/pay-api/src/pay_api/utils/trace.py b/pay-api/src/pay_api/utils/trace.py deleted file mode 100644 index 55704e71a..000000000 --- a/pay-api/src/pay_api/utils/trace.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright © 2024 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Bring in the Tracer.""" -from sbc_common_components.tracing.api_tracer import ApiTracer -from sbc_common_components.tracing.api_tracing import ApiTracing - - -# initialize tracer -API_TRACER = ApiTracer('Payment Services') -tracing = ApiTracing( # pylint: disable=invalid-name; lower case name as used by convention in most Flask apps - API_TRACER.tracer) diff --git a/pay-api/src/pay_api/utils/util.py b/pay-api/src/pay_api/utils/util.py index 3e282f878..631d31f8f 100755 --- a/pay-api/src/pay_api/utils/util.py +++ b/pay-api/src/pay_api/utils/util.py @@ -25,12 +25,13 @@ from holidays.constants import GOVERNMENT, OPTIONAL, PUBLIC from holidays.countries import Canada import pytz -from dpath import util as dpath_util +from dpath import get as dpath_get from flask import current_app from pay_api.services.code import Code as CodeService from .constants import DT_SHORT_FORMAT +from .converter import Converter from .enums import Code, CorpType, Product, StatementFrequency @@ -75,7 +76,7 @@ def get_str_by_path(payload: Dict, path: str) -> str: return None try: - raw = dpath_util.get(payload, path) + raw = dpath_get(payload, path) return str(raw) if raw is not None else raw except (IndexError, KeyError, TypeError): return None @@ -244,6 +245,22 @@ def string_to_date(date_val: str, dt_format: str = DT_SHORT_FORMAT): return datetime.strptime(date_val, dt_format).date() +def string_to_decimal(val: str): + """Return decimal from string.""" + if val is None: + return None + + return Decimal(val) + + +def string_to_int(val: str): + """Return int from string.""" + if val is None: + return None + + return int(val) + + def get_quantized(amount: float) -> Decimal: """Return rounded decimal. (Default = ROUND_HALF_EVEN).""" return Decimal(amount).quantize(Decimal('1.00')) @@ -265,3 +282,11 @@ def get_topic_for_corp_type(corp_type: str): if product_code == Product.BUSINESS.value: return current_app.config.get('BUSINESS_PAY_TOPIC') return None + + +def unstructure_schema_items(schema, items): + """Return unstructured results by schema.""" + results = [schema.from_row(item) for item in items] + converter = Converter() + + return converter.unstructure(results) diff --git a/pay-api/tests/conftest.py b/pay-api/tests/conftest.py index a49509987..e6b606c2e 100755 --- a/pay-api/tests/conftest.py +++ b/pay-api/tests/conftest.py @@ -14,10 +14,12 @@ """Common setup and fixtures for the py-test suite used by this service.""" +import os + import pytest from flask_migrate import Migrate, upgrade from sqlalchemy import event, text -from sqlalchemy.schema import DropConstraint, MetaData +from sqlalchemy_utils import create_database, database_exists, drop_database from pay_api import create_app from pay_api import jwt as _jwt @@ -29,7 +31,6 @@ def app(): """Return a session-wide application configured in TEST mode.""" _app = create_app('testing') - return _app @@ -61,7 +62,6 @@ def publish(self, *args, **kwargs): def app_request(): """Return a session-wide application configured in TEST mode.""" _app = create_app('testing') - return _app @@ -84,98 +84,56 @@ def client_ctx(app): yield _client -@pytest.fixture(scope='session') +@pytest.fixture(scope='session', autouse=True) def db(app): # pylint: disable=redefined-outer-name, invalid-name - """Return a session-wide initialised database. - - Drops all existing tables - Meta follows Postgres FKs - """ + """Return a session-wide initialised database.""" with app.app_context(): - # Clear out views - view_sql = """SELECT table_name FROM information_schema.views - WHERE table_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(view_sql))]: - try: - sess.execute(text('DROP VIEW public.%s ;' % seq)) - print('DROP VIEW public.%s ' % seq) - except Exception as err: # NOQA pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # Clear out any existing tables - metadata = MetaData(_db.engine) - metadata.reflect() - for table in metadata.tables.values(): - for fk in table.foreign_keys: # pylint: disable=invalid-name - _db.engine.execute(DropConstraint(fk.constraint)) - metadata.drop_all() - _db.drop_all() - - sequence_sql = """SELECT sequence_name FROM information_schema.sequences - WHERE sequence_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(sequence_sql))]: - try: - sess.execute(text('DROP SEQUENCE public.%s ;' % seq)) - print('DROP SEQUENCE public.%s ' % seq) - except Exception as err: # NOQA pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # ############################################ - # There are 2 approaches, an empty database, or the same one that the app will use - # create the tables - # _db.create_all() - # or - # Use Alembic to load all of the DB revisions including supporting lookup data - # This is the path we'll use in legal_api!! - - # even though this isn't referenced directly, it sets up the internal configs that upgrade needs + if database_exists(_db.engine.url): + drop_database(_db.engine.url) + create_database(_db.engine.url) + _db.session().execute(text('SET TIME ZONE "UTC";')) Migrate(app, _db) upgrade() - return _db -@pytest.fixture(scope='function', autouse=True) -def session(app, db): # pylint: disable=redefined-outer-name, invalid-name +@pytest.fixture(scope='function') +def session(db, app): # pylint: disable=redefined-outer-name, invalid-name """Return a function-scoped session.""" with app.app_context(): - conn = db.engine.connect() - txn = conn.begin() - - options = dict(bind=conn, binds={}) - sess = db.create_scoped_session(options=options) - - # establish a SAVEPOINT just before beginning the test - # (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) - sess.begin_nested() - - @event.listens_for(sess(), 'after_transaction_end') - def restart_savepoint(sess2, trans): # pylint: disable=unused-variable - # Detecting whether this is indeed the nested transaction of the test - if trans.nested and not trans._parent.nested: # pylint: disable=protected-access - # Handle where test DOESN'T session.commit(), - sess2.expire_all() - sess.begin_nested() + with db.engine.connect() as conn: + transaction = conn.begin() + sess = db._make_scoped_session(dict(bind=conn)) # pylint: disable=protected-access + # Establish SAVEPOINT (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) + nested = sess.begin_nested() + old_session = db.session + db.session = sess + db.session.commit = nested.commit + db.session.rollback = nested.rollback + + @event.listens_for(sess, 'after_transaction_end') + def restart_savepoint(sess2, trans): # pylint: disable=unused-variable + nonlocal nested + if trans.nested: + # Handle where test DOESN'T session.commit() + sess2.expire_all() + nested = sess.begin_nested() + # When using a SAVEPOINT via the Session.begin_nested() or Connection.begin_nested() methods, + # the transaction object returned must be used to commit or rollback the SAVEPOINT. + # Calling the Session.commit() or Connection.commit() methods will always commit the + # outermost transaction; this is a SQLAlchemy 2.0 specific behavior that is + # reversed from the 1.x series + db.session = sess + db.session.commit = nested.commit + db.session.rollback = nested.rollback - db.session = sess - - sql = text('select 1') - sess.execute(sql) - - yield sess - - # Cleanup - sess.remove() - # This instruction rollsback any commit that were executed in the tests. - txn.rollback() - conn.close() + try: + yield db.session + finally: + db.session.remove() + transaction.rollback() + event.remove(sess, 'after_transaction_end', restart_savepoint) + db.session = old_session @pytest.fixture() @@ -266,7 +224,6 @@ def auto(docker_services, app): @pytest.fixture(scope='session') def docker_compose_files(pytestconfig): """Get the docker-compose.yml absolute path.""" - import os return [ os.path.join(str(pytestconfig.rootdir), 'tests/docker', 'docker-compose.yml') ] diff --git a/pay-api/tests/docker/nginx.conf b/pay-api/tests/docker/nginx.conf index 8a41cc603..a10266c82 100644 --- a/pay-api/tests/docker/nginx.conf +++ b/pay-api/tests/docker/nginx.conf @@ -37,7 +37,7 @@ http { set $last_path_component example1; rewrite ^/reports-api/api/v1/(.*) /$1 break; proxy_set_header Prefer example=$last_path_component; - proxy_set_header Accept "application/json"; #TODO + proxy_set_header Accept "application/json"; proxy_pass http://reports:4010/; } diff --git a/pay-api/tests/unit/api/fas/test_refund.py b/pay-api/tests/unit/api/fas/test_refund.py index 4d7f35ed0..de9f3530f 100644 --- a/pay-api/tests/unit/api/fas/test_refund.py +++ b/pay-api/tests/unit/api/fas/test_refund.py @@ -29,7 +29,7 @@ fake = Faker() -def test_refund_routing_slips(client, jwt): +def test_refund_routing_slips(session, client, jwt): """Assert refund works for routing slips.""" payload = get_routing_slip_request() token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value, Role.FAS_REFUND.value]), @@ -88,7 +88,7 @@ def test_refund_routing_slips(client, jwt): assert rv.json.get('status') == RoutingSlipStatus.REFUND_AUTHORIZED.value -def test_refund_routing_slips_reject(client, jwt): +def test_refund_routing_slips_reject(session, client, jwt): """Assert refund works for routing slips.""" payload = get_routing_slip_request() token = jwt.create_jwt( @@ -117,7 +117,7 @@ def test_refund_routing_slips_reject(client, jwt): assert rv.json.get('status') == RoutingSlipStatus.ACTIVE.value -def test_refund_routing_slips_zero_dollar_error(client, jwt): +def test_refund_routing_slips_zero_dollar_error(session, client, jwt): """Assert zero dollar refund fails.""" payload = get_routing_slip_request(cheque_receipt_numbers=[('1234567890', PaymentMethod.CHEQUE.value, 0.00)]) token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value, Role.FAS_REFUND.value]), diff --git a/pay-api/tests/unit/api/fas/test_routing_slip.py b/pay-api/tests/unit/api/fas/test_routing_slip.py index 5885819e9..64eab26fe 100755 --- a/pay-api/tests/unit/api/fas/test_routing_slip.py +++ b/pay-api/tests/unit/api/fas/test_routing_slip.py @@ -44,7 +44,7 @@ ]), get_routing_slip_request(cheque_receipt_numbers=[('0001', PaymentMethod.CASH.value, 2000)]) ]) -def test_create_routing_slips(client, jwt, payload): +def test_create_routing_slips(session, client, jwt, payload): """Assert that the endpoint returns 200.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -59,7 +59,7 @@ def test_create_routing_slips(client, jwt, payload): assert len(allowed_statuses) == len(RoutingSlipStatusTransitionService.STATUS_TRANSITIONS.get('ACTIVE')) -def test_create_routing_slips_search(client, jwt, app): +def test_create_routing_slips_search(session, client, jwt, app): """Assert that the search works.""" claims = get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value]) token = jwt.create_jwt(claims, token_header) @@ -137,7 +137,7 @@ def test_create_routing_slips_search(client, jwt, app): assert len(items) == 0 -def test_link_routing_slip_parent_is_a_child(client, jwt): +def test_link_routing_slip_parent_is_a_child(session, client, jwt): """Assert linking to a child fails.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_LINK.value, Role.FAS_SEARCH.value]), token_header) @@ -163,7 +163,7 @@ def test_link_routing_slip_parent_is_a_child(client, jwt): assert rv.status_code == 400 -def test_link_nsf(client, jwt): +def test_link_nsf(session, client, jwt): """Assert linking to a child fails.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_LINK.value, Role.FAS_SEARCH.value, Role.FAS_EDIT.value]), @@ -186,7 +186,7 @@ def test_link_nsf(client, jwt): assert rv.json.get('title') == 'Routing Slip cannot be linked.' -def test_link_routing_slip_invalid_status(client, jwt, app): +def test_link_routing_slip_invalid_status(session, client, jwt, app): """Assert that the linking of routing slip works as expected.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_LINK.value, Role.FAS_SEARCH.value, Role.FAS_REFUND.value, @@ -225,7 +225,7 @@ def test_link_routing_slip_invalid_status(client, jwt, app): assert rv.json.get('type') == 'RS_IN_INVALID_STATUS', 'parent is invalid.' -def test_link_routing_slip(client, jwt, app): +def test_link_routing_slip(session, client, jwt, app): """Assert that the linking of routing slip works as expected.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_LINK.value, Role.FAS_SEARCH.value, Role.FAS_EDIT.value]), @@ -328,7 +328,7 @@ def test_link_routing_slip(client, jwt, app): assert rv.status_code == 200, 'parent can have transactions' -def test_create_routing_slips_search_with_folio_number(client, jwt, app): +def test_create_routing_slips_search_with_folio_number(session, client, jwt, app): """Assert that the search works.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -398,7 +398,7 @@ def test_create_routing_slips_search_with_folio_number(client, jwt, app): assert len(items[0].get('invoices')) == 2, 'folio alone works.' -def test_create_routing_slips_search_with_receipt(client, jwt, app): +def test_create_routing_slips_search_with_receipt(session, client, jwt, app): """Assert that the search works.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -455,7 +455,7 @@ def test_create_routing_slips_search_with_receipt(client, jwt, app): @pytest.mark.parametrize('payload', [ get_routing_slip_request(number='559555333'), ]) -def test_create_routing_slips_unauthorized(client, jwt, payload): +def test_create_routing_slips_unauthorized(session, client, jwt, payload): """Assert that the endpoint returns 401 for users with no fas_editor role.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_USER.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -475,7 +475,7 @@ def test_create_routing_slips_unauthorized(client, jwt, payload): @pytest.mark.parametrize('payload', [ get_routing_slip_request(number='559555333'), ]) -def test_create_routing_slips_invalid_digits(client, jwt, payload): +def test_create_routing_slips_invalid_digits(session, client, jwt, payload): """Assert POST returns 400 when providing invalid routing slip number.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_USER.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -486,7 +486,7 @@ def test_create_routing_slips_invalid_digits(client, jwt, payload): current_app.config['ALLOW_LEGACY_ROUTING_SLIPS'] = True -def test_get_routing_slips_invalid_digits(client, jwt, app): +def test_get_routing_slips_invalid_digits(session, client, jwt, app): """Assert GET returns 400 when providing invalid routing slip number.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_VIEW.value, Role.FAS_CREATE.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -500,7 +500,7 @@ def test_get_routing_slips_invalid_digits(client, jwt, app): @pytest.mark.parametrize('payload', [ get_routing_slip_request(number='206380883'), ]) -def test_routing_slips_for_errors(client, jwt, payload): +def test_routing_slips_for_errors(session, client, jwt, payload): """Assert that the endpoint returns 401 for users with no fas_editor role.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -521,7 +521,7 @@ def test_routing_slips_for_errors(client, jwt, payload): assert rv.status_code == 204 -def test_update_routing_slip_status(client, jwt, app): +def test_update_routing_slip_status(session, client, jwt, app): """Assert that the endpoint returns 200.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_EDIT.value, Role.FAS_VIEW.value]), token_header) @@ -552,7 +552,7 @@ def test_update_routing_slip_status(client, jwt, app): assert rv.status_code == 400 -def test_routing_slip_report(client, jwt, app): +def test_routing_slip_report(session, client, jwt, app): """Assert that the endpoint returns 201.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_REPORTS.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -561,7 +561,7 @@ def test_routing_slip_report(client, jwt, app): assert rv.status_code == 201 -def test_create_comment_with_valid_routing_slips(client, jwt): +def test_create_comment_with_valid_routing_slips(session, client, jwt): """Assert that the endpoint returns 201.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -577,7 +577,7 @@ def test_create_comment_with_valid_routing_slips(client, jwt): assert rv.json.get('submitterDisplayName') -def test_create_comment_with_invalid_routing_slips(client, jwt): +def test_create_comment_with_invalid_routing_slips(session, client, jwt): """Assert that the endpoint returns 201.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -589,7 +589,7 @@ def test_create_comment_with_invalid_routing_slips(client, jwt): assert rv.status_code == 400 -def test_create_comment_with_invalid_body_request(client, jwt): +def test_create_comment_with_invalid_body_request(session, client, jwt): """Assert that the endpoint returns 201.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -602,7 +602,7 @@ def test_create_comment_with_invalid_body_request(client, jwt): assert rv.status_code == 400 -def test_create_comment_with_valid_comment_schema(client, jwt): +def test_create_comment_with_valid_comment_schema(session, client, jwt): """Assert that the endpoint returns 201 for valid comment schema.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -618,7 +618,7 @@ def test_create_comment_with_valid_comment_schema(client, jwt): assert rv.json.get('submitterDisplayName') -def test_create_comment_with_invalid_comment_schema(client, jwt): +def test_create_comment_with_invalid_comment_schema(session, client, jwt): """Assert that the endpoint returns 400 for invalid comment schema.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -634,7 +634,7 @@ def test_create_comment_with_invalid_comment_schema(client, jwt): assert rv.status_code == 400 -def test_create_comment_with_valid_comment_bcrs_schema(client, jwt): +def test_create_comment_with_valid_comment_bcrs_schema(session, client, jwt): """Assert that the endpoint returns 201 for valid comment schema.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -650,7 +650,7 @@ def test_create_comment_with_valid_comment_bcrs_schema(client, jwt): assert rv.json.get('submitterDisplayName') -def test_create_comment_with_invalid_comment_bcrs_schema(client, jwt): +def test_create_comment_with_invalid_comment_bcrs_schema(session, client, jwt): """Assert that the endpoint returns 201 for valid comment schema.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -666,7 +666,7 @@ def test_create_comment_with_invalid_comment_bcrs_schema(client, jwt): assert rv.status_code == 400 -def test_get_valid_comments(client, jwt): +def test_get_valid_comments(session, client, jwt): """Assert that the endpoint returns 200.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -696,7 +696,7 @@ def test_get_valid_comments(client, jwt): assert rv.status_code == 400 -def test_get_invalid_comments(client, jwt): +def test_get_invalid_comments(session, client, jwt): """Assert that the endpoint returns 400 based on conditions.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -706,7 +706,7 @@ def test_get_invalid_comments(client, jwt): assert rv.json.get('type') == 'FAS_INVALID_ROUTING_SLIP_NUMBER' -def test_create_routing_slips_invalid_number(client, jwt, app): +def test_create_routing_slips_invalid_number(session, client, jwt, app): """Assert that the rs number validation works.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -720,7 +720,7 @@ def test_create_routing_slips_invalid_number(client, jwt, app): assert rv.status_code == 400 -def test_update_routing_slip_writeoff(client, jwt, app): +def test_update_routing_slip_writeoff(session, client, jwt, app): """Assert that the endpoint returns 200.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_EDIT.value, Role.FAS_VIEW.value]), token_header) @@ -748,7 +748,7 @@ def test_update_routing_slip_writeoff(client, jwt, app): assert rv.status_code == 200 -def test_create_routing_slip_null_cheque_date(client, jwt, app): +def test_create_routing_slip_null_cheque_date(session, client, jwt, app): """Assert that the endpoint returns invalid request for null payment date.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_EDIT.value, Role.FAS_VIEW.value]), token_header) @@ -772,7 +772,7 @@ def test_create_routing_slip_null_cheque_date(client, jwt, app): assert rv.status_code == 400 -def test_routing_slip_link_attempt(client, jwt, app): +def test_routing_slip_link_attempt(session, client, jwt, app): """12033 - Scenario 3. Routing slip is Completed, attempt to be linked. @@ -803,7 +803,7 @@ def test_routing_slip_link_attempt(client, jwt, app): assert rv.status_code == 400 -def test_routing_slip_status_to_nsf_attempt(client, jwt, app): +def test_routing_slip_status_to_nsf_attempt(session, client, jwt, app): """12033 - Scenario 4. Routing slip in Completed, @@ -831,7 +831,7 @@ def test_routing_slip_status_to_nsf_attempt(client, jwt, app): assert rv.status_code == 200, 'status changed successfully.' -def test_routing_slip_void(client, jwt, app): +def test_routing_slip_void(session, client, jwt, app): """For testing void routing slips.""" # Create routing slip. token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_LINK.value, @@ -872,7 +872,7 @@ def test_routing_slip_void(client, jwt, app): assert rv.json.get('remainingAmount') == 0 -def test_routing_slip_correction(client, jwt, app): +def test_routing_slip_correction(session, client, jwt, app): """For testing correction of routing slips.""" # Create routing slip. token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_LINK.value, diff --git a/pay-api/tests/unit/api/test_account.py b/pay-api/tests/unit/api/test_account.py index 468b725de..0aeda1e47 100755 --- a/pay-api/tests/unit/api/test_account.py +++ b/pay-api/tests/unit/api/test_account.py @@ -475,6 +475,21 @@ def test_update_pad_account_when_cfs_up(session, client, jwt, app): assert rv.status_code == 200 +def test_switch_eft_account_when_cfs_up(session, client, jwt, app, admin_users_mock): + """Assert that the payment records are created with 202.""" + token = jwt.create_jwt(get_claims(role=Role.SYSTEM.value), token_header) + headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} + rv = client.post('/api/v1/accounts', + data=json.dumps(get_basic_account_payload(payment_method=PaymentMethod.PAD.value)), + headers=headers) + auth_account_id = rv.json.get('accountId') + rv = client.put(f'/api/v1/accounts/{auth_account_id}', + data=json.dumps(get_basic_account_payload(payment_method=PaymentMethod.EFT.value)), + headers=headers) + + assert rv.status_code == 200 + + def test_update_online_banking_account_when_cfs_down(session, client, jwt, app): """Assert that the payment records are created with 200, as there is no CFS update.""" token = jwt.create_jwt(get_claims(role=Role.SYSTEM.value), token_header) diff --git a/pay-api/tests/unit/api/test_cors_preflight.py b/pay-api/tests/unit/api/test_cors_preflight.py index 9f61d4563..67ee915a5 100644 --- a/pay-api/tests/unit/api/test_cors_preflight.py +++ b/pay-api/tests/unit/api/test_cors_preflight.py @@ -227,7 +227,17 @@ def test_preflight_eft_shortnames(app, client, jwt, session): rv = client.options('/api/v1/eft-shortnames/1', headers={'Access-Control-Request-Method': 'GET'}) assert rv.status_code == 200 - assert_access_control_headers(rv, '*', 'GET, PATCH') + assert_access_control_headers(rv, '*', 'GET') + + rv = client.options('/api/v1/eft-shortnames/summaries', + headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET') + + rv = client.options('/api/v1/eft-shortnames/1/links', + headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET, POST') rv = client.options('/api/v1/eft-shortnames/1/transactions', headers={'Access-Control-Request-Method': 'GET'}) diff --git a/pay-api/tests/unit/api/test_eft_short_names.py b/pay-api/tests/unit/api/test_eft_short_names.py index 802bc14ba..7ce382585 100755 --- a/pay-api/tests/unit/api/test_eft_short_names.py +++ b/pay-api/tests/unit/api/test_eft_short_names.py @@ -19,23 +19,29 @@ import json from datetime import datetime +from decimal import Decimal +import pytest from flask import current_app from pay_api.models import EFTCredit as EFTCreditModel from pay_api.models import EFTFile as EFTFileModel from pay_api.models import EFTShortnames as EFTShortnamesModel +from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import Payment as PaymentModel +from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Receipt as ReceiptModel from pay_api.utils.enums import ( - EFTFileLineType, EFTProcessStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, Role) + EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, InvoiceStatus, PaymentMethod, PaymentStatus, Role, + StatementFrequency) from tests.utilities.base_test import ( - factory_eft_file, factory_eft_shortname, factory_invoice, factory_payment_account, get_claims, token_header) + factory_eft_file, factory_eft_shortname, factory_eft_shortname_link, factory_invoice, factory_payment_account, + factory_statement, factory_statement_invoices, factory_statement_settings, get_claims, token_header) -def test_patch_eft_short_name(session, client, jwt, app): - """Assert that an EFT short name account id can be patched.""" +def test_create_eft_short_name_link(session, client, jwt, app): + """Assert that an EFT short name link can be created.""" token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value], username='IDIR/JSMITH'), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -43,64 +49,167 @@ def test_patch_eft_short_name(session, client, jwt, app): auth_account_id='1234').save() short_name = factory_eft_shortname(short_name='TESTSHORTNAME').save() - rv = client.patch(f'/api/v1/eft-shortnames/{short_name.id}', - data=json.dumps({'accountId': '1234'}), - headers=headers) - shortname_dict = rv.json + rv = client.post(f'/api/v1/eft-shortnames/{short_name.id}/links', + data=json.dumps({'accountId': '1234'}), + headers=headers) + link_dict = rv.json assert rv.status_code == 200 - assert shortname_dict is not None - assert shortname_dict['id'] is not None - assert shortname_dict['shortName'] == 'TESTSHORTNAME' - assert shortname_dict['accountId'] == '1234' - assert shortname_dict['linkedBy'] == 'IDIR/JSMITH' + assert link_dict is not None + assert link_dict['id'] is not None + assert link_dict['shortNameId'] == short_name.id + assert link_dict['statusCode'] == EFTShortnameStatus.PENDING.value + assert link_dict['accountId'] == '1234' + assert link_dict['updatedBy'] == 'IDIR/JSMITH' date_format = '%Y-%m-%dT%H:%M:%S.%f' - assert datetime.strptime(shortname_dict['linkedOn'], date_format).date() == datetime.now().date() + assert datetime.strptime(link_dict['updatedOn'], date_format).date() == datetime.now().date() -def test_patch_eft_short_name_validation(session, client, jwt, app): - """Assert that invalid request is returned for existing short name.""" - token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value]), token_header) +def test_create_eft_short_name_link_validation(session, client, jwt, app): + """Assert that invalid request is returned for existing short name link.""" + token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value], + username='IDIR/JSMITH'), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} - short_name = factory_eft_shortname(short_name='TESTSHORTNAME', auth_account_id='1234').save() + short_name = factory_eft_shortname(short_name='TESTSHORTNAME').save() + factory_eft_shortname_link( + short_name_id=short_name.id, + auth_account_id='1234', + updated_by='IDIR/JSMITH' + ).save() # Assert requires an auth account id for mapping - rv = client.patch(f'/api/v1/eft-shortnames/{short_name.id}', - data=json.dumps({}), - headers=headers) + rv = client.post(f'/api/v1/eft-shortnames/{short_name.id}/links', + data=json.dumps({}), + headers=headers) - shortname_dict = rv.json + link_dict = rv.json assert rv.status_code == 400 - assert shortname_dict['type'] == 'EFT_SHORT_NAME_ACCOUNT_ID_REQUIRED' + assert link_dict['type'] == 'EFT_SHORT_NAME_ACCOUNT_ID_REQUIRED' - # Assert cannot update short name with an existing mapped account id - rv = client.patch(f'/api/v1/eft-shortnames/{short_name.id}', - data=json.dumps({'accountId': '2222'}), - headers=headers) + # Assert cannot create link to an existing mapped account id + rv = client.post(f'/api/v1/eft-shortnames/{short_name.id}/links', + data=json.dumps({'accountId': '1234'}), + headers=headers) - shortname_dict = rv.json + link_dict = rv.json assert rv.status_code == 400 - assert shortname_dict['type'] == 'EFT_SHORT_NAME_ALREADY_MAPPED' + assert link_dict['type'] == 'EFT_SHORT_NAME_ALREADY_MAPPED' -def assert_short_name(result_dict: dict, short_name: EFTShortnamesModel, transaction: EFTTransactionModel): - """Assert short name result.""" +def test_eft_short_name_unlink(session, client, jwt, app): + """Assert that an EFT short name unlinking and basic state validation.""" + token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value], + username='IDIR/JSMITH'), token_header) + headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} + account = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='1234').save() + + short_name = factory_eft_shortname(short_name='TESTSHORTNAME').save() + short_name_link = EFTShortnameLinksModel( + eft_short_name_id=short_name.id, + status_code=EFTShortnameStatus.LINKED.value, + auth_account_id=account.auth_account_id + ).save() + + # Assert cannot unlink an account not in PENDING status + rv = client.delete(f'/api/v1/eft-shortnames/{short_name.id}/links/{short_name_link.id}', + headers=headers) + + link_dict = rv.json + assert rv.status_code == 400 + assert link_dict['type'] == 'EFT_SHORT_NAME_LINK_INVALID_STATUS' + + short_name_link.status_code = EFTShortnameStatus.PENDING.value + short_name_link.save() + + # Assert we can delete a short name link that is pending + rv = client.delete(f'/api/v1/eft-shortnames/{short_name.id}/links/{short_name_link.id}', + headers=headers) + assert rv.status_code == 202 + + +def test_get_eft_short_name_links(session, client, jwt, app): + """Assert that short name links can be retrieved.""" + token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value], + username='IDIR/JSMITH'), token_header) + headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} + account = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='1234', + name='ABC-123', + branch_name='123').save() + short_name = factory_eft_shortname(short_name='TESTSHORTNAME').save() + + invoice = factory_invoice(account, payment_method_code=PaymentMethod.EFT.value, + total=50, paid=0).save() + statement_settings = factory_statement_settings(payment_account_id=account.id, + frequency=StatementFrequency.MONTHLY.value) + statement = factory_statement(payment_account_id=account.id, + frequency=StatementFrequency.MONTHLY.value, + statement_settings_id=statement_settings.id) + factory_statement_invoices(statement_id=statement.id, invoice_id=invoice.id) + + # Assert an empty result set is properly returned + rv = client.get(f'/api/v1/eft-shortnames/{short_name.id}/links', + headers=headers) + + link_dict = rv.json + assert rv.status_code == 200 + assert link_dict is not None + assert link_dict['items'] is not None + assert len(link_dict['items']) == 0 + + # Create a short name link + rv = client.post(f'/api/v1/eft-shortnames/{short_name.id}/links', + data=json.dumps({'accountId': account.auth_account_id}), + headers=headers) + + link_dict = rv.json + assert rv.status_code == 200 + + # Assert link is returned in the result + rv = client.get(f'/api/v1/eft-shortnames/{short_name.id}/links', + headers=headers) + + link_list_dict = rv.json + assert rv.status_code == 200 + assert link_list_dict is not None + assert link_list_dict['items'] is not None + assert len(link_list_dict['items']) == 1 + + link = link_list_dict['items'][0] + assert link['accountId'] == account.auth_account_id + assert link['id'] == link_dict['id'] + assert link['shortNameId'] == short_name.id + assert link['accountId'] == account.auth_account_id + assert link['accountName'] == 'ABC' + assert link['accountBranch'] == '123' + assert link['amountOwing'] == invoice.total + assert link['statementId'] == statement.id + assert link['statusCode'] == EFTShortnameStatus.PENDING.value + assert link['updatedBy'] == 'IDIR/JSMITH' + + +def assert_short_name_summary(result_dict: dict, + short_name: EFTShortnamesModel, + transaction: EFTTransactionModel, + expected_credits_remaining: Decimal, + expected_linked_accounts_count: int): + """Assert short name summary result.""" date_format = '%Y-%m-%dT%H:%M:%S' + assert result_dict['id'] == short_name.id assert result_dict['shortName'] == short_name.short_name - assert result_dict['accountId'] == short_name.auth_account_id - assert result_dict['depositAmount'] == transaction.deposit_amount_cents / 100 - assert datetime.strptime(result_dict['depositDate'], date_format) == transaction.deposit_date - assert result_dict['transactionId'] == transaction.id - assert datetime.strptime(result_dict['transactionDate'], date_format) == transaction.transaction_date + assert result_dict['creditsRemaining'] == expected_credits_remaining + assert result_dict['linkedAccountsCount'] == expected_linked_accounts_count + assert datetime.strptime(result_dict['lastPaymentReceivedDate'], date_format) == transaction.deposit_date -def test_search_eft_short_names(session, client, jwt, app): - """Assert that EFT short names can be searched.""" +def test_eft_short_name_summaries(session, client, jwt, app): + """Assert that EFT short names summaries can be searched.""" token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} # Assert initial search returns empty items - rv = client.get('/api/v1/eft-shortnames', headers=headers) + rv = client.get('/api/v1/eft-shortnames/summaries', headers=headers) assert rv.status_code == 200 result_dict = rv.json @@ -109,167 +218,122 @@ def test_search_eft_short_names(session, client, jwt, app): assert len(result_dict['items']) == 0 # create test data - payment_account = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, - auth_account_id='1234', - name='ABC-123', - branch_name='123').save() - - eft_file: EFTFileModel = factory_eft_file() - short_name_1 = factory_eft_shortname(short_name='TESTSHORTNAME1').save() - short_name_2 = factory_eft_shortname(short_name='TESTSHORTNAME2', auth_account_id='1234').save() - - # short_name_1 transactions to test getting first payment - s1_transaction1: EFTTransactionModel = EFTTransactionModel( - line_type=EFTFileLineType.TRANSACTION.value, - line_number=1, - file_id=eft_file.id, - status_code=EFTProcessStatus.COMPLETED.value, - transaction_date=datetime(2024, 1, 5, 2, 30), - deposit_date=datetime(2024, 1, 6, 10, 5), - deposit_amount_cents=10150, - short_name_id=short_name_1.id - - ).save() - - # Identical to transaction 1 should not return duplicate short name rows - partitioned by transaction date, id - EFTTransactionModel( - line_type=EFTFileLineType.TRANSACTION.value, - line_number=1, - file_id=eft_file.id, - status_code=EFTProcessStatus.COMPLETED.value, - transaction_date=datetime(2024, 1, 5, 2, 30), - deposit_date=datetime(2024, 1, 6, 10, 5), - deposit_amount_cents=10250, - short_name_id=short_name_1.id - - ).save() + factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='1234', + name='ABC-123', + branch_name='123').save() - EFTTransactionModel( - line_type=EFTFileLineType.TRANSACTION.value, - line_number=1, - file_id=eft_file.id, - status_code=EFTProcessStatus.COMPLETED.value, - transaction_date=datetime(2024, 1, 10, 2, 30), - deposit_date=datetime(2024, 1, 11, 10, 5), - deposit_amount_cents=30150, - short_name_id=short_name_1.id - ).save() + short_name_1, s1_transaction1, short_name_2, s2_transaction1 = create_eft_summary_search_data() - # short_name_2 transactions - to test date filters - s2_transaction1: EFTTransactionModel = EFTTransactionModel( - line_type=EFTFileLineType.TRANSACTION.value, - line_number=1, - file_id=eft_file.id, - status_code=EFTProcessStatus.COMPLETED.value, - transaction_date=datetime(2024, 1, 15, 2, 30), - deposit_date=datetime(2024, 1, 16, 10, 5), - deposit_amount_cents=30250, - short_name_id=short_name_2.id + # Assert short name search brings back both short names + rv = client.get('/api/v1/eft-shortnames/summaries?shortName=SHORT', headers=headers) + assert rv.status_code == 200 - ).save() + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 1 + assert result_dict['stateTotal'] == 2 + assert result_dict['total'] == 2 + assert result_dict['limit'] == 10 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 2 + assert_short_name_summary(result_dict['items'][0], + short_name_1, s1_transaction1, 204.0, 0) + assert_short_name_summary(result_dict['items'][1], + short_name_2, s2_transaction1, 302.5, 1) - # Assert search returns unlinked short names - rv = client.get('/api/v1/eft-shortnames?state=UNLINKED', headers=headers) + # Assert short name search brings back first short name + rv = client.get('/api/v1/eft-shortnames/summaries?shortName=name1', headers=headers) assert rv.status_code == 200 result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 1 + assert result_dict['stateTotal'] == 2 assert result_dict['total'] == 1 assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME1' - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) + assert_short_name_summary(result_dict['items'][0], + short_name_1, s1_transaction1, 204.0, 0) - # Assert search returns linked short names with payment account name that has a branch - rv = client.get('/api/v1/eft-shortnames?state=LINKED', headers=headers) + # Assert search linked accounts count + rv = client.get('/api/v1/eft-shortnames/summaries?linkedAccountsCount=0', headers=headers) assert rv.status_code == 200 result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 1 + assert result_dict['stateTotal'] == 2 assert result_dict['total'] == 1 assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME2' - assert result_dict['items'][0]['accountName'] == 'ABC' - assert result_dict['items'][0]['accountBranch'] == '123' - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name_summary(result_dict['items'][0], + short_name_1, s1_transaction1, 204.0, 0) - # Assert search account name - rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountName=BC', headers=headers) + rv = client.get('/api/v1/eft-shortnames/summaries?linkedAccountsCount=1', headers=headers) assert rv.status_code == 200 result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 1 + assert result_dict['stateTotal'] == 2 assert result_dict['total'] == 1 assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert result_dict['items'][0]['accountName'] == 'ABC' - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name_summary(result_dict['items'][0], + short_name_2, s2_transaction1, 302.5, 1) - # Assert search account branch - rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountBranch=2', headers=headers) + # Assert search by payment received date + rv = client.get('/api/v1/eft-shortnames/summaries?' + 'paymentReceivedStartDate=2024-01-16&paymentReceivedEndDate=2024-01-16', headers=headers) assert rv.status_code == 200 result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 1 + assert result_dict['stateTotal'] == 2 assert result_dict['total'] == 1 assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert result_dict['items'][0]['accountName'] == 'ABC' - assert result_dict['items'][0]['accountBranch'] == '123' - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) - - # Update payment account to not have a branch name - payment_account.name = 'ABC' - payment_account.branch_name = None - payment_account.save() + assert_short_name_summary(result_dict['items'][0], + short_name_2, s2_transaction1, 302.5, 1) - # Assert search returns linked short names with payment account name that has no branch - rv = client.get('/api/v1/eft-shortnames?state=LINKED', headers=headers) + # Assert search by short name id + rv = client.get(f'/api/v1/eft-shortnames/summaries?shortNameId={short_name_2.id}', headers=headers) assert rv.status_code == 200 result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 1 + assert result_dict['stateTotal'] == 2 assert result_dict['total'] == 1 assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME2' - assert result_dict['items'][0]['accountName'] == 'ABC' - assert result_dict['items'][0]['accountBranch'] is None - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name_summary(result_dict['items'][0], + short_name_2, s2_transaction1, 302.5, 1) - # Assert search account name - rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountName=BC', headers=headers) + # Assert search by remaining credits + rv = client.get('/api/v1/eft-shortnames/summaries?creditsRemaining=204', headers=headers) assert rv.status_code == 200 result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 1 + assert result_dict['stateTotal'] == 2 assert result_dict['total'] == 1 assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert result_dict['items'][0]['accountName'] == 'ABC' - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name_summary(result_dict['items'][0], + short_name_1, s1_transaction1, 204.0, 0) # Assert search query by no state will return all records - rv = client.get('/api/v1/eft-shortnames', headers=headers) + rv = client.get('/api/v1/eft-shortnames/summaries', headers=headers) assert rv.status_code == 200 result_dict = rv.json @@ -280,11 +344,13 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 2 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) - assert_short_name(result_dict['items'][1], short_name_2, s2_transaction1) + assert_short_name_summary(result_dict['items'][0], + short_name_1, s1_transaction1, 204.0, 0) + assert_short_name_summary(result_dict['items'][1], + short_name_2, s2_transaction1, 302.5, 1) # Assert search pagination - page 1 works - rv = client.get('/api/v1/eft-shortnames?page=1&limit=1', headers=headers) + rv = client.get('/api/v1/eft-shortnames/summaries?page=1&limit=1', headers=headers) assert rv.status_code == 200 result_dict = rv.json @@ -295,10 +361,11 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 1 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) + assert_short_name_summary(result_dict['items'][0], + short_name_1, s1_transaction1, 204.0, 0) # Assert search pagination - page 2 works - rv = client.get('/api/v1/eft-shortnames?page=2&limit=1', headers=headers) + rv = client.get('/api/v1/eft-shortnames/summaries?page=2&limit=1', headers=headers) assert rv.status_code == 200 result_dict = rv.json @@ -309,25 +376,253 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 1 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name_summary(result_dict['items'][0], + short_name_2, s2_transaction1, 302.5, 1) + + +def create_eft_summary_search_data(): + """Create seed data for EFT summary searches.""" + eft_file: EFTFileModel = factory_eft_file() + short_name_1 = factory_eft_shortname(short_name='TESTSHORTNAME1').save() + short_name_2 = factory_eft_shortname(short_name='TESTSHORTNAME2').save() + factory_eft_shortname_link( + short_name_id=short_name_2.id, + auth_account_id='1234', + updated_by='IDIR/JSMITH' + ).save() + + # short_name_1 transactions to test getting first payment + s1_transaction1: EFTTransactionModel = EFTTransactionModel( + line_type=EFTFileLineType.TRANSACTION.value, + line_number=1, + file_id=eft_file.id, + status_code=EFTProcessStatus.COMPLETED.value, + transaction_date=datetime(2024, 1, 5, 2, 30), + deposit_date=datetime(2024, 1, 6, 10, 5), + deposit_amount_cents=10150, + short_name_id=short_name_1.id + ).save() + + EFTCreditModel(eft_file_id=eft_file.id, + short_name_id=s1_transaction1.short_name_id, + amount=s1_transaction1.deposit_amount_cents / 100, + remaining_amount=s1_transaction1.deposit_amount_cents / 100 + ).save() + + # Identical to transaction 1 should not return duplicate short name rows - partitioned by transaction date, id + s1_transaction2: EFTTransactionModel = EFTTransactionModel( + line_type=EFTFileLineType.TRANSACTION.value, + line_number=1, + file_id=eft_file.id, + status_code=EFTProcessStatus.COMPLETED.value, + transaction_date=datetime(2024, 1, 5, 2, 30), + deposit_date=datetime(2024, 1, 6, 10, 5), + deposit_amount_cents=10250, + short_name_id=short_name_1.id + + ).save() + + EFTCreditModel(eft_file_id=eft_file.id, + short_name_id=s1_transaction2.short_name_id, + amount=s1_transaction2.deposit_amount_cents / 100, + remaining_amount=s1_transaction2.deposit_amount_cents / 100 + ).save() + + EFTTransactionModel( + line_type=EFTFileLineType.TRANSACTION.value, + line_number=1, + file_id=eft_file.id, + status_code=EFTProcessStatus.COMPLETED.value, + transaction_date=datetime(2024, 1, 10, 2, 30), + deposit_date=datetime(2024, 1, 5, 10, 5), + deposit_amount_cents=30150, + short_name_id=short_name_1.id + ).save() + + # short_name_2 transactions - to test date filters + s2_transaction1: EFTTransactionModel = EFTTransactionModel( + line_type=EFTFileLineType.TRANSACTION.value, + line_number=1, + file_id=eft_file.id, + status_code=EFTProcessStatus.COMPLETED.value, + transaction_date=datetime(2024, 1, 15, 2, 30), + deposit_date=datetime(2024, 1, 16, 10, 5), + deposit_amount_cents=30250, + short_name_id=short_name_2.id + + ).save() + + EFTCreditModel(eft_file_id=eft_file.id, + short_name_id=s2_transaction1.short_name_id, + amount=s2_transaction1.deposit_amount_cents / 100, + remaining_amount=s2_transaction1.deposit_amount_cents / 100 + ).save() + + return short_name_1, s1_transaction1, short_name_2, s2_transaction1 + + +def create_eft_search_data(): + """Create seed data for EFT searches.""" + payment_account_1 = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='1111', + name='ABC-1111', + branch_name='111').save() + payment_account_2 = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='2222', + name='DEF-2222', + branch_name='222').save() + payment_account_3 = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='3333', + name='GHI-3333', + branch_name='333').save() + + # Create unlinked short name + short_name_unlinked = factory_eft_shortname(short_name='TESTSHORTNAME1').save() + + # Create single linked short name + short_name_linked = factory_eft_shortname(short_name='TESTSHORTNAME2').save() + factory_eft_shortname_link( + short_name_id=short_name_linked.id, + auth_account_id=payment_account_1.auth_account_id, + updated_by='IDIR/JSMITH' + ).save() + # Create statement with multiple invoices + s1_invoice_1 = factory_invoice(payment_account_1, payment_method_code=PaymentMethod.EFT.value, + total=50, paid=0).save() + s1_invoice_2 = factory_invoice(payment_account_1, payment_method_code=PaymentMethod.EFT.value, + total=100.50, paid=0).save() + s1_settings = factory_statement_settings(payment_account_id=payment_account_1.id, + frequency=StatementFrequency.MONTHLY.value) + statement_1 = factory_statement(payment_account_id=payment_account_1.id, + frequency=StatementFrequency.MONTHLY.value, + statement_settings_id=s1_settings.id) + factory_statement_invoices(statement_id=statement_1.id, invoice_id=s1_invoice_1.id) + factory_statement_invoices(statement_id=statement_1.id, invoice_id=s1_invoice_2.id) + + # Create multi account linked short name + short_name_multi_linked = factory_eft_shortname(short_name='TESTSHORTNAME3').save() + factory_eft_shortname_link( + short_name_id=short_name_multi_linked.id, + auth_account_id=payment_account_2.auth_account_id, + updated_by='IDIR/JSMITH' + ).save() + factory_eft_shortname_link( + short_name_id=short_name_multi_linked.id, + auth_account_id=payment_account_3.auth_account_id, + updated_by='IDIR/JSMITH' + ).save() + + s2_settings = factory_statement_settings(payment_account_id=payment_account_2.id, + frequency=StatementFrequency.MONTHLY.value) + statement_2 = factory_statement(payment_account_id=payment_account_2.id, + frequency=StatementFrequency.MONTHLY.value, + statement_settings_id=s2_settings.id) + + s3_settings = factory_statement_settings(payment_account_id=payment_account_3.id, + frequency=StatementFrequency.MONTHLY.value) + statement_3 = factory_statement(payment_account_id=payment_account_3.id, + frequency=StatementFrequency.MONTHLY.value, + statement_settings_id=s3_settings.id) + s3_invoice_1 = factory_invoice(payment_account_3, payment_method_code=PaymentMethod.EFT.value, + total=33.33, paid=0).save() + factory_statement_invoices(statement_id=statement_3.id, invoice_id=s3_invoice_1.id) + + return { + 'single-linked': {'short_name': short_name_linked, + 'accounts': [payment_account_1], + 'statement_summary': [{'statement_id': statement_1.id, 'owing_amount': 150.50}]}, + 'multi-linked': {'short_name': short_name_multi_linked, + 'accounts': [payment_account_2, payment_account_3], + 'statement_summary': [{'statement_id': statement_2.id, 'owing_amount': 0}, + {'statement_id': statement_3.id, 'owing_amount': 33.33}]}, + 'unlinked': {'short_name': short_name_unlinked, + 'accounts': [], + 'statement_summary': None} + } + + +def assert_short_name(result_dict: dict, short_name: EFTShortnamesModel, + payment_account: PaymentAccountModel = None, + statement_summary=None): + """Assert short name result.""" + assert result_dict['shortName'] == short_name.short_name + + if not payment_account: + assert result_dict['accountId'] is None + assert result_dict['accountName'] is None + assert result_dict['accountBranch'] is None + else: + assert result_dict['accountId'] == payment_account.auth_account_id + assert payment_account.name.startswith(result_dict['accountName']) + assert result_dict['accountBranch'] == payment_account.branch_name + + if not statement_summary: + assert result_dict['amountOwing'] == 0 + assert result_dict['statementId'] is None + else: + assert result_dict['amountOwing'] == statement_summary['owing_amount'] + assert result_dict['statementId'] == statement_summary['statement_id'] + + +def test_search_eft_short_names(session, client, jwt, app): + """Assert that EFT short names can be searched.""" + token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value]), token_header) + headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} + + # Assert initial search returns empty items + rv = client.get('/api/v1/eft-shortnames', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['items'] is not None + assert len(result_dict['items']) == 0 + + # create test data + data_dict = create_eft_search_data() + + # Assert search returns unlinked short names + rv = client.get('/api/v1/eft-shortnames?state=UNLINKED', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 1 + assert result_dict['stateTotal'] == 1 + assert result_dict['total'] == 1 + assert result_dict['limit'] == 10 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 1 + assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME1' + assert_short_name(result_dict['items'][0], data_dict['unlinked']['short_name']) - # Assert search text brings back both short names - rv = client.get('/api/v1/eft-shortnames?shortName=SHORT', headers=headers) + # Assert search returns linked short names with payment account name that has a branch + rv = client.get('/api/v1/eft-shortnames?state=LINKED', headers=headers) assert rv.status_code == 200 result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 assert result_dict['stateTotal'] == 2 - assert result_dict['total'] == 2 + assert result_dict['total'] == 3 assert result_dict['limit'] == 10 assert result_dict['items'] is not None - assert len(result_dict['items']) == 2 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) - assert_short_name(result_dict['items'][1], short_name_2, s2_transaction1) + assert len(result_dict['items']) == 3 + assert_short_name(result_dict['items'][0], + data_dict['single-linked']['short_name'], + data_dict['single-linked']['accounts'][0], + data_dict['single-linked']['statement_summary'][0]) + assert_short_name(result_dict['items'][1], + data_dict['multi-linked']['short_name'], + data_dict['multi-linked']['accounts'][0], + data_dict['multi-linked']['statement_summary'][0]) + assert_short_name(result_dict['items'][2], + data_dict['multi-linked']['short_name'], + data_dict['multi-linked']['accounts'][1], + data_dict['multi-linked']['statement_summary'][1]) - # Assert search text brings back one short name - rv = client.get('/api/v1/eft-shortnames?shortName=name1', headers=headers) + # Assert search account name + rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountName=BC', headers=headers) assert rv.status_code == 200 result_dict = rv.json @@ -338,11 +633,13 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) + assert_short_name(result_dict['items'][0], + data_dict['single-linked']['short_name'], + data_dict['single-linked']['accounts'][0], + data_dict['single-linked']['statement_summary'][0]) - # Assert search transaction date - rv = client.get('/api/v1/eft-shortnames?transactionStartDate=2024-01-04&transactionEndDate=2024-01-14', - headers=headers) + # Assert search account branch + rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountBranch=2', headers=headers) assert rv.status_code == 200 result_dict = rv.json @@ -353,68 +650,87 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) + assert_short_name(result_dict['items'][0], + data_dict['multi-linked']['short_name'], + data_dict['multi-linked']['accounts'][0], + data_dict['multi-linked']['statement_summary'][0]) - # Assert search transaction date - rv = client.get('/api/v1/eft-shortnames?transactionStartDate=2024-01-04&transactionEndDate=2024-01-15', - headers=headers) + # Assert search query by no state will return all records + rv = client.get('/api/v1/eft-shortnames', headers=headers) assert rv.status_code == 200 result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 2 - assert result_dict['total'] == 2 + assert result_dict['stateTotal'] == 3 + assert result_dict['total'] == 4 assert result_dict['limit'] == 10 assert result_dict['items'] is not None - assert len(result_dict['items']) == 2 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) - assert_short_name(result_dict['items'][1], short_name_2, s2_transaction1) + assert len(result_dict['items']) == 4 + assert_short_name(result_dict['items'][0], + data_dict['unlinked']['short_name']) + assert_short_name(result_dict['items'][1], + data_dict['single-linked']['short_name'], + data_dict['single-linked']['accounts'][0], + data_dict['single-linked']['statement_summary'][0]) + assert_short_name(result_dict['items'][2], + data_dict['multi-linked']['short_name'], + data_dict['multi-linked']['accounts'][0], + data_dict['multi-linked']['statement_summary'][0]) + assert_short_name(result_dict['items'][3], + data_dict['multi-linked']['short_name'], + data_dict['multi-linked']['accounts'][1], + data_dict['multi-linked']['statement_summary'][1]) - # Assert search transaction date - rv = client.get('/api/v1/eft-shortnames?depositStartDate=2024-01-16&depositEndDate=2024-01-16', headers=headers) + # Assert search pagination - page 1 works + rv = client.get('/api/v1/eft-shortnames?page=1&limit=1', headers=headers) assert rv.status_code == 200 result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 2 - assert result_dict['total'] == 1 - assert result_dict['limit'] == 10 + assert result_dict['stateTotal'] == 3 + assert result_dict['total'] == 4 + assert result_dict['limit'] == 1 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], + data_dict['unlinked']['short_name']) - # Assert search deposit amount - rv = client.get('/api/v1/eft-shortnames?depositAmount=101.50', headers=headers) + # Assert search pagination - page 2 works + rv = client.get('/api/v1/eft-shortnames?page=2&limit=1', headers=headers) assert rv.status_code == 200 result_dict = rv.json assert result_dict is not None - assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 2 - assert result_dict['total'] == 1 - assert result_dict['limit'] == 10 + assert result_dict['page'] == 2 + assert result_dict['stateTotal'] == 3 + assert result_dict['total'] == 4 + assert result_dict['limit'] == 1 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) + assert_short_name(result_dict['items'][0], + data_dict['single-linked']['short_name'], + data_dict['single-linked']['accounts'][0], + data_dict['single-linked']['statement_summary'][0]) - # Assert search account id - rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountId=1234', headers=headers) + # Assert search text brings back one short name + rv = client.get('/api/v1/eft-shortnames?shortName=name1', headers=headers) assert rv.status_code == 200 result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 1 + assert result_dict['stateTotal'] == 3 assert result_dict['total'] == 1 assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], + data_dict['unlinked']['short_name']) - # Assert search account id list - rv = client.get('/api/v1/eft-shortnames?accountIdList=1,1234', headers=headers) + # Assert search account id + rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountId=1111', headers=headers) assert rv.status_code == 200 result_dict = rv.json @@ -425,12 +741,16 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME2' - assert result_dict['items'][0]['accountName'] == 'ABC' - assert result_dict['items'][0]['accountBranch'] is None - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], + data_dict['single-linked']['short_name'], + data_dict['single-linked']['accounts'][0], + data_dict['single-linked']['statement_summary'][0]) +@pytest.mark.skip(reason='This needs to be re-thought, the create cfs invoice job should be handling receipt creation' + 'and creating invoice references when payments are mapped, ' + 'it should wait until 6 pm before marking invoices as PAID' + 'Otherwise calls to CFS could potentially fail and the two systems would go out of sync.') def test_apply_eft_short_name_credits(session, client, jwt, app): """Assert that credits are applied to invoices when short name is mapped to an account.""" token = jwt.create_jwt(get_claims(roles=[Role.STAFF.value, Role.MANAGE_EFT.value]), token_header) @@ -503,12 +823,7 @@ def test_apply_eft_short_name_credits(session, client, jwt, app): assert payment.invoice_amount == invoice_1_paid assert payment.paid_amount == invoice_1_paid - invoice_reference_1 = invoice_1.references[0] - assert invoice_reference_1 is not None - assert invoice_reference_1.invoice_id == invoice_1.id - assert invoice_reference_1.invoice_number == payment.invoice_number - assert invoice_reference_1.invoice_number == payment.invoice_number - assert invoice_reference_1.status_code == InvoiceReferenceStatus.COMPLETED.value + assert not invoice_1.references # Assert details of partially paid invoice invoice_2_paid = 150 @@ -534,9 +849,4 @@ def test_apply_eft_short_name_credits(session, client, jwt, app): assert payment.invoice_amount == 200 assert payment.paid_amount == invoice_2_paid - invoice_reference_2 = invoice_2.references[0] - assert invoice_reference_2 is not None - assert invoice_reference_2.invoice_id == invoice_2.id - assert invoice_reference_2.invoice_number == payment.invoice_number - assert invoice_reference_2.invoice_number == payment.invoice_number - assert invoice_reference_2.status_code == InvoiceReferenceStatus.ACTIVE.value + assert not invoice_2.references diff --git a/pay-api/tests/unit/api/test_eft_transactions.py b/pay-api/tests/unit/api/test_eft_transactions.py index ec201fc97..108edd0a5 100755 --- a/pay-api/tests/unit/api/test_eft_transactions.py +++ b/pay-api/tests/unit/api/test_eft_transactions.py @@ -20,35 +20,47 @@ from datetime import datetime from pay_api.models import EFTCredit as EFTCreditModel +from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceLinkModel from pay_api.models import EFTFile as EFTFileModel from pay_api.models import EFTShortnames as EFTShortnamesModel from pay_api.models import EFTTransaction as EFTTransactionModel -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, PaymentMethod, Role +from pay_api.utils.enums import ( + EFTCreditInvoiceStatus, EFTFileLineType, EFTProcessStatus, InvoiceStatus, PaymentMethod, Role, StatementFrequency) from tests.utilities.base_test import ( - factory_eft_file, factory_eft_shortname, factory_payment_account, get_claims, token_header) + factory_eft_file, factory_eft_shortname, factory_eft_shortname_link, factory_invoice, factory_payment_account, + factory_statement, factory_statement_invoices, factory_statement_settings, get_claims, token_header) -def assert_transaction(result_dict: dict, short_name: EFTShortnamesModel, transaction: EFTTransactionModel): - """Assert short name result.""" +def assert_funds_received(result_dict: dict, short_name: EFTShortnamesModel, transaction: EFTTransactionModel): + """Assert funds received rows.""" date_format = '%Y-%m-%dT%H:%M:%S' - assert result_dict['id'] == transaction.id + assert result_dict['transactionId'] == transaction.id + assert not result_dict['accountId'] + assert not result_dict['accountName'] + assert not result_dict['accountBranch'] + assert not result_dict['statementId'] assert result_dict['shortNameId'] == short_name.id - assert result_dict['depositAmount'] == transaction.deposit_amount_cents / 100 - assert datetime.strptime(result_dict['depositDate'], date_format) == transaction.deposit_date - assert datetime.strptime(result_dict['transactionDate'], date_format) == transaction.transaction_date + assert result_dict['transactionAmount'] == transaction.deposit_amount_cents / 100 + assert datetime.strptime(result_dict['transactionDate'], date_format) == transaction.deposit_date + assert result_dict['transactionDescription'] == 'Funds Received' -def test_search_short_name_transactions(session, client, jwt, app): - """Assert that EFT short names transactions can be searched.""" +def test_search_short_name_funds_received(session, client, jwt, app): + """Assert that EFT short names funds received can be searched.""" token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} # create test data payment_account = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, - auth_account_id='1234').save() + auth_account_id='1234', name='ABC-BRANCH', branch_name='BRANCH').save() eft_file: EFTFileModel = factory_eft_file() short_name_1 = factory_eft_shortname(short_name='TESTSHORTNAME1').save() - short_name_2 = factory_eft_shortname(short_name='TESTSHORTNAME2', auth_account_id='1234').save() + short_name_2 = factory_eft_shortname(short_name='TESTSHORTNAME2').save() + factory_eft_shortname_link( + short_name_id=short_name_2.id, + auth_account_id='1234', + updated_by='IDIR/JSMITH' + ).save() # short_name_1 transactions s1_transaction1: EFTTransactionModel = EFTTransactionModel( @@ -56,8 +68,8 @@ def test_search_short_name_transactions(session, client, jwt, app): line_number=1, file_id=eft_file.id, status_code=EFTProcessStatus.COMPLETED.value, - transaction_date=datetime(2024, 1, 5, 2, 30), - deposit_date=datetime(2024, 1, 6, 10, 5), + transaction_date=datetime(2024, 1, 4, 2, 30), + deposit_date=datetime(2024, 1, 5, 10, 5), deposit_amount_cents=10150, short_name_id=short_name_1.id @@ -100,7 +112,7 @@ def test_search_short_name_transactions(session, client, jwt, app): ).save() # short name 2 credit - s2_credit1 = EFTCreditModel( + EFTCreditModel( eft_file_id=eft_file.id, eft_transaction_id=s2_transaction1.id, short_name_id=short_name_2.id, @@ -108,7 +120,8 @@ def test_search_short_name_transactions(session, client, jwt, app): amount=302.50, remaining_amount=302.50 ).save() - s2_credit2 = EFTCreditModel( + + EFTCreditModel( eft_file_id=eft_file.id, eft_transaction_id=s2_transaction1.id, short_name_id=short_name_2.id, @@ -129,11 +142,11 @@ def test_search_short_name_transactions(session, client, jwt, app): assert result_dict['items'] is not None assert len(result_dict['items']) == 3 # Most recent transaction date first - assert_transaction(result_dict['items'][0], short_name_1, s1_transaction3) - assert_transaction(result_dict['items'][1], short_name_1, s1_transaction2) - assert_transaction(result_dict['items'][2], short_name_1, s1_transaction1) + assert_funds_received(result_dict['items'][0], short_name_1, s1_transaction3) + assert_funds_received(result_dict['items'][1], short_name_1, s1_transaction2) + assert_funds_received(result_dict['items'][2], short_name_1, s1_transaction1) - # Assert search returns unlinked short names + # Assert search returns funds received rows rv = client.get(f'/api/v1/eft-shortnames/{short_name_2.id}/transactions', headers=headers) assert rv.status_code == 200 @@ -144,5 +157,124 @@ def test_search_short_name_transactions(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert result_dict['remainingCredit'] == (s2_credit1.remaining_amount + s2_credit2.remaining_amount) - assert_transaction(result_dict['items'][0], short_name_2, s2_transaction1) + assert_funds_received(result_dict['items'][0], short_name_2, s2_transaction1) + + +def test_search_short_name_funds_applied(session, client, jwt, app): + """Assert that EFT short names funds applied can be searched.""" + token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value]), token_header) + headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} + + # create test data + payment_account = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='1234', name='ABC-BRANCH', branch_name='BRANCH').save() + eft_file: EFTFileModel = factory_eft_file() + short_name = factory_eft_shortname(short_name='TESTSHORTNAME1').save() + + # Set up scenario where statement invoices have been paid via EFT + # EFT Transactions from TDI17 have been processed and EFT Credit records have been created and used. + transaction1: EFTTransactionModel = EFTTransactionModel( + line_type=EFTFileLineType.TRANSACTION.value, + line_number=1, + file_id=eft_file.id, + status_code=EFTProcessStatus.COMPLETED.value, + transaction_date=datetime(2024, 1, 4, 2, 30), + deposit_date=datetime(2024, 1, 5, 10, 5), + deposit_amount_cents=10150, + short_name_id=short_name.id).save() + t1_eft_credit = EFTCreditModel( + eft_file_id=eft_file.id, + eft_transaction_id=transaction1.id, + short_name_id=short_name.id, + payment_account_id=payment_account.id, + amount=101.50, + remaining_amount=0).save() + + transaction2 = EFTTransactionModel( + line_type=EFTFileLineType.TRANSACTION.value, + line_number=1, + file_id=eft_file.id, + status_code=EFTProcessStatus.COMPLETED.value, + transaction_date=datetime(2024, 1, 5, 3, 30), + deposit_date=datetime(2024, 1, 6, 10, 5), + deposit_amount_cents=10000, + short_name_id=short_name.id).save() + + t2_eft_credit = EFTCreditModel( + eft_file_id=eft_file.id, + eft_transaction_id=transaction2.id, + short_name_id=short_name.id, + payment_account_id=payment_account.id, + amount=10.25, + remaining_amount=10.25).save() + + # Create statement and invoices that have been paid + statement_payment_date = datetime(2024, 1, 10, 9, 0) + invoice_1 = factory_invoice(payment_account, payment_method_code=PaymentMethod.EFT.value, + total=105.50, paid=0, status_code=InvoiceStatus.PAID.value).save() + invoice_1.payment_date = statement_payment_date + invoice_1.save() + invoice_2 = factory_invoice(payment_account, payment_method_code=PaymentMethod.EFT.value, + total=96.00, paid=0, status_code=InvoiceStatus.PAID.value).save() + invoice_2.payment_date = statement_payment_date + invoice_2.save() + + statement_settings = factory_statement_settings(payment_account_id=payment_account.id, + frequency=StatementFrequency.MONTHLY.value) + statement = factory_statement(payment_account_id=payment_account.id, + frequency=StatementFrequency.MONTHLY.value, + statement_settings_id=statement_settings.id) + factory_statement_invoices(statement_id=statement.id, invoice_id=invoice_1.id) + factory_statement_invoices(statement_id=statement.id, invoice_id=invoice_2.id) + + # Establish EFT Credit to invoice links for applied funds and amount + EFTCreditInvoiceLinkModel( + eft_credit_id=t1_eft_credit.id, + invoice_id=invoice_1.id, + amount=101.50, + status_code=EFTCreditInvoiceStatus.COMPLETED.value + ).save() + + # Testing transactions query where first set of credits were insufficient and a second set was applied + # This should just roll up as a total amount paid + EFTCreditInvoiceLinkModel( + eft_credit_id=t2_eft_credit.id, + invoice_id=invoice_1.id, + amount=4.00, + status_code=EFTCreditInvoiceStatus.COMPLETED.value + ).save() + + # Remaining credit paid to invoice 2 + EFTCreditInvoiceLinkModel( + eft_credit_id=t2_eft_credit.id, + invoice_id=invoice_2.id, + amount=96.00, + status_code=EFTCreditInvoiceStatus.COMPLETED.value + ).save() + + # Assert search returns funds received and funds applied rows + rv = client.get(f'/api/v1/eft-shortnames/{short_name.id}/transactions', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 1 + assert result_dict['total'] == 3 + assert result_dict['limit'] == 10 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 3 + + statement_paid = result_dict['items'][0] + date_format = '%Y-%m-%dT%H:%M:%S' + assert not statement_paid['transactionId'] + assert statement_paid['statementId'] == statement.id + assert statement_paid['accountId'] == payment_account.auth_account_id + assert statement_paid['accountName'] == 'ABC' + assert statement_paid['accountBranch'] == 'BRANCH' + assert statement_paid['shortNameId'] == short_name.id + assert statement_paid['transactionAmount'] == 201.50 + assert datetime.strptime(statement_paid['transactionDate'], date_format) == statement_payment_date + assert statement_paid['transactionDescription'] == 'Statement Paid' + + assert_funds_received(result_dict['items'][1], short_name, transaction2) + assert_funds_received(result_dict['items'][2], short_name, transaction1) diff --git a/pay-api/tests/unit/api/test_fee.py b/pay-api/tests/unit/api/test_fee.py index d79cde939..f8cd3928e 100755 --- a/pay-api/tests/unit/api/test_fee.py +++ b/pay-api/tests/unit/api/test_fee.py @@ -18,6 +18,7 @@ """ import json from datetime import date, timedelta +from decimal import Decimal from pay_api.models import CorpType, FeeCode, FeeSchedule, FilingType from pay_api.schemas import utils as schema_utils @@ -287,10 +288,10 @@ def factory_filing_type_model( def factory_fee_model( fee_code: str, - amount: int): + amount: float): """Return the fee code model.""" fee_code_master = FeeCode(code=fee_code, - amount=amount) + amount=Decimal(str(amount))) fee_code_master.save() return fee_code_master diff --git a/pay-api/tests/unit/api/test_ops.py b/pay-api/tests/unit/api/test_ops.py index 5a833f85f..c081b9aa5 100755 --- a/pay-api/tests/unit/api/test_ops.py +++ b/pay-api/tests/unit/api/test_ops.py @@ -17,6 +17,8 @@ Test-Suite to ensure that the /ops endpoint is working as expected. """ +from sqlalchemy.exc import SQLAlchemyError +from pay_api.models import db def test_ops_healthz_success(client): @@ -27,12 +29,13 @@ def test_ops_healthz_success(client): assert rv.json == {'message': 'api is healthy'} -def test_ops_healthz_fail(app_request): - """Assert that the service is unhealthy if a connection toThe database cannot be made.""" - app_request.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://does:not@exist:5432/nada' +def test_ops_healthz_fail(app_request, monkeypatch): + """Assert that the service is unhealthy if a connection to the database cannot be made.""" + def db_error(_): + raise SQLAlchemyError(1, 2, code='42') + monkeypatch.setattr(db.session, 'execute', db_error) with app_request.test_client() as client: rv = client.get('/ops/healthz') - assert rv.status_code == 500 assert rv.json == {'message': 'api is down'} diff --git a/pay-api/tests/unit/api/test_payment_request.py b/pay-api/tests/unit/api/test_payment_request.py index 784e3f685..aa06aa51f 100755 --- a/pay-api/tests/unit/api/test_payment_request.py +++ b/pay-api/tests/unit/api/test_payment_request.py @@ -320,7 +320,7 @@ def test_payment_creation_with_routing_slip(session, client, jwt, app): assert schema_utils.validate(rv.json, 'invoice')[0] -def test_zero_dollar_payment_creation_with_existing_routing_slip(client, jwt): +def test_zero_dollar_payment_creation_with_existing_routing_slip(session, client, jwt): """Assert that the endpoint returns 201.""" claims = get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value, Role.STAFF.value, 'make_payment']) token = jwt.create_jwt(claims, token_header) @@ -349,7 +349,7 @@ def test_zero_dollar_payment_creation_with_existing_routing_slip(client, jwt): get_payment_request(), get_payment_request_without_bn() ]) -def test_payment_creation_with_existing_routing_slip(client, jwt, payment_requests): +def test_payment_creation_with_existing_routing_slip(session, client, jwt, payment_requests): """Assert that the endpoint returns 201.""" claims = get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value, Role.STAFF.value, 'make_payment']) token = jwt.create_jwt(claims, token_header) @@ -375,7 +375,7 @@ def test_payment_creation_with_existing_routing_slip(client, jwt, payment_reques assert items[0].get('remainingAmount') == payload.get('payments')[0].get('paidAmount') - total -def test_payment_creation_with_existing_invalid_routing_slip_invalid(client, jwt): +def test_payment_creation_with_existing_invalid_routing_slip_invalid(session, client, jwt): """Assert that the endpoint returns 201.""" claims = get_claims( roles=[Role.FAS_CREATE.value, Role.FAS_EDIT.value, Role.STAFF.value, 'make_payment', Role.FAS_LINK.value]) diff --git a/pay-api/tests/unit/api/test_receipt.py b/pay-api/tests/unit/api/test_receipt.py index 2aa7603aa..a5ba50b64 100644 --- a/pay-api/tests/unit/api/test_receipt.py +++ b/pay-api/tests/unit/api/test_receipt.py @@ -140,18 +140,19 @@ def test_receipt_creation_with_invalid_request(session, client, jwt, app): headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} rv = client.post('/api/v1/payment-requests', data=json.dumps(get_payment_request()), headers=headers) - inovice_id = rv.json.get('id') + invoice_id = rv.json.get('id') redirect_uri = 'http%3A//localhost%3A8080/coops-web/transactions%3Ftransaction_id%3Dabcd' receipt_number = '123451' - rv = client.post(f'/api/v1/payment-requests/{inovice_id}/transactions?redirect_uri={redirect_uri}', data=None, + rv = client.post(f'/api/v1/payment-requests/{invoice_id}/transactions?redirect_uri={redirect_uri}', + data=json.dumps({}), headers=headers) txn_id = rv.json.get('id') - client.patch(f'/api/v1/payment-requests/{inovice_id}/transactions/{txn_id}', + client.patch(f'/api/v1/payment-requests/{invoice_id}/transactions/{txn_id}', data=json.dumps({'receipt_number': receipt_number}), headers=headers) filing_data = { 'corpName': 'CP0001234' } - rv = client.post(f'/api/v1/payment-requests/{inovice_id}/receipts', data=json.dumps(filing_data), + rv = client.post(f'/api/v1/payment-requests/{invoice_id}/receipts', data=json.dumps(filing_data), headers=headers) assert rv.status_code == 400 assert rv.json.get('type') == 'INVALID_REQUEST' diff --git a/pay-api/tests/unit/models/test_eft_credit_invoice_link.py b/pay-api/tests/unit/models/test_eft_credit_invoice_link.py index 5931edd14..42c7db5ee 100644 --- a/pay-api/tests/unit/models/test_eft_credit_invoice_link.py +++ b/pay-api/tests/unit/models/test_eft_credit_invoice_link.py @@ -18,7 +18,7 @@ """ from pay_api.models import EFTCredit, EFTCreditInvoiceLink, EFTFile, EFTShortnames, EFTTransaction -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus +from pay_api.utils.enums import EFTCreditInvoiceStatus, EFTFileLineType, EFTProcessStatus, EFTShortnameStatus from tests.utilities.base_test import factory_invoice, factory_payment_account @@ -34,6 +34,7 @@ def test_eft_credit_invoice_link(session): eft_short_name = EFTShortnames() eft_short_name.auth_account_id = payment_account.auth_account_id + eft_short_name.status_code = EFTShortnameStatus.LINKED.value eft_short_name.short_name = 'TESTSHORTNAME' eft_short_name.save() @@ -62,9 +63,13 @@ def test_eft_credit_invoice_link(session): eft_credit_invoice_link = EFTCreditInvoiceLink() eft_credit_invoice_link.invoice_id = invoice.id eft_credit_invoice_link.eft_credit_id = eft_credit.id + eft_credit_invoice_link.status_code = EFTCreditInvoiceStatus.PENDING.value + eft_credit_invoice_link.amount = 50.00 eft_credit_invoice_link.save() eft_credit_invoice_link = EFTCreditInvoiceLink.find_by_id(eft_credit_invoice_link.id) assert eft_credit_invoice_link.id is not None assert eft_credit_invoice_link.eft_credit_id == eft_credit.id assert eft_credit_invoice_link.invoice_id == invoice.id + assert eft_credit_invoice_link.status_code == EFTCreditInvoiceStatus.PENDING.value + assert eft_credit_invoice_link.amount == 50.00 diff --git a/pay-api/tests/unit/models/test_eft_credits.py b/pay-api/tests/unit/models/test_eft_credits.py index d63171920..614c230b7 100644 --- a/pay-api/tests/unit/models/test_eft_credits.py +++ b/pay-api/tests/unit/models/test_eft_credits.py @@ -20,7 +20,7 @@ from typing import List from pay_api.models import EFTCredit, EFTFile, EFTShortnames, EFTTransaction -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus from tests.utilities.base_test import factory_payment_account @@ -33,6 +33,7 @@ def test_eft_credits(session): eft_short_name = EFTShortnames() eft_short_name.auth_account_id = payment_account.auth_account_id + eft_short_name.status_code = EFTShortnameStatus.LINKED.value eft_short_name.short_name = 'TESTSHORTNAME' eft_short_name.save() diff --git a/pay-api/tests/unit/models/test_eft_gl_transfer.py b/pay-api/tests/unit/models/test_eft_gl_transfer.py index 83cec3d6e..f4d29b040 100644 --- a/pay-api/tests/unit/models/test_eft_gl_transfer.py +++ b/pay-api/tests/unit/models/test_eft_gl_transfer.py @@ -20,7 +20,7 @@ from pay_api.models.eft_gl_transfers import EFTGLTransfer as EFTGLTransferModel from pay_api.models.eft_short_names import EFTShortnames as EFTShortnamesModel -from pay_api.utils.enums import EFTGlTransferType +from pay_api.utils.enums import EFTGlTransferType, EFTShortnameStatus from tests.utilities.base_test import factory_invoice, factory_payment, factory_payment_account @@ -41,6 +41,7 @@ def create_short_name_data(): """Create shortname seed data for test.""" eft_short_name = EFTShortnamesModel() eft_short_name.short_name = 'ABC' + eft_short_name.status_code = EFTShortnameStatus.LINKED.value eft_short_name.save() return eft_short_name diff --git a/pay-api/tests/unit/models/test_eft_short_name_links.py b/pay-api/tests/unit/models/test_eft_short_name_links.py new file mode 100644 index 000000000..8cfd3e7ce --- /dev/null +++ b/pay-api/tests/unit/models/test_eft_short_name_links.py @@ -0,0 +1,73 @@ +# Copyright © 2023 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests to assure the EFT Short names model. + +Test-Suite to ensure that the EFT Short names model is working as expected. +""" +from datetime import datetime + +from pay_api.models.eft_short_names import EFTShortnames as EFTShortnamesModel +from pay_api.models.eft_short_name_links import EFTShortnameLinks as EFTShortnameLinksModel +from pay_api.utils.enums import EFTShortnameStatus + + +def create_short_name_data(): + """Create shortname seed data for test.""" + eft_short_name = EFTShortnamesModel() + eft_short_name.short_name = 'ABC' + eft_short_name.save() + + return eft_short_name + + +def test_eft_short_name_defaults(session): + """Assert eft short name link defaults are stored.""" + eft_short_name = create_short_name_data() + eft_short_name_link = EFTShortnameLinksModel() + eft_short_name_link.eft_short_name_id = eft_short_name.id + eft_short_name_link.status_code = EFTShortnameStatus.PENDING.value + eft_short_name_link.auth_account_id = '1234' + eft_short_name_link.save() + + assert eft_short_name_link.id is not None + assert eft_short_name_link.eft_short_name_id == eft_short_name.id + assert eft_short_name_link.created_on.date() == datetime.now().date() + assert eft_short_name_link.auth_account_id == '1234' + assert eft_short_name_link.status_code == EFTShortnameStatus.PENDING.value + assert eft_short_name_link.updated_by is None + assert eft_short_name_link.updated_by_name is None + assert eft_short_name_link.updated_on is None + + +def test_eft_short_name_all_attributes(session): + """Assert eft short name link defaults are stored.""" + eft_short_name = create_short_name_data() + eft_short_name_link = EFTShortnameLinksModel() + eft_short_name_link.eft_short_name_id = eft_short_name.id + eft_short_name_link.status_code = EFTShortnameStatus.PENDING.value + eft_short_name_link.auth_account_id = '1234' + eft_short_name_link.updated_by_name = 'name' + eft_short_name_link.updated_by = 'userid' + eft_short_name_link.updated_on = datetime.now() + eft_short_name_link.save() + + assert eft_short_name_link.id is not None + assert eft_short_name_link.eft_short_name_id == eft_short_name.id + assert eft_short_name_link.created_on.date() == datetime.now().date() + assert eft_short_name_link.auth_account_id == '1234' + assert eft_short_name_link.status_code == EFTShortnameStatus.PENDING.value + assert eft_short_name_link.updated_by == 'userid' + assert eft_short_name_link.updated_by_name == 'name' + assert eft_short_name_link.updated_on.date() == datetime.now().date() diff --git a/pay-api/tests/unit/models/test_eft_short_names.py b/pay-api/tests/unit/models/test_eft_short_names.py index 6d160fa35..6c0ea03d7 100644 --- a/pay-api/tests/unit/models/test_eft_short_names.py +++ b/pay-api/tests/unit/models/test_eft_short_names.py @@ -21,8 +21,8 @@ from pay_api.models.eft_short_names import EFTShortnames as EFTShortnamesModel -def test_eft_short_name_defaults(session): - """Assert eft short names defaults are stored.""" +def test_eft_short_name_model(session): + """Assert eft short names are stored.""" eft_short_name = EFTShortnamesModel() eft_short_name.short_name = 'ABC' eft_short_name.save() @@ -30,19 +30,3 @@ def test_eft_short_name_defaults(session): assert eft_short_name.id is not None assert eft_short_name.short_name == 'ABC' assert eft_short_name.created_on.date() == datetime.now().date() - assert eft_short_name.auth_account_id is None - - -def test_eft_short_names_all_attributes(session): - """Assert all eft short names attributes are stored.""" - eft_short_name = EFTShortnamesModel() - eft_short_name.short_name = 'ABC' - eft_short_name.auth_account_id = '1234' - eft_short_name.save() - - assert eft_short_name.id is not None - - eft_short_name = EFTShortnamesModel.find_by_id(eft_short_name.id) - assert eft_short_name.short_name == 'ABC' - assert eft_short_name.auth_account_id == '1234' - assert eft_short_name.created_on.date() == datetime.now().date() diff --git a/pay-api/tests/unit/services/test_direct_pay_service.py b/pay-api/tests/unit/services/test_direct_pay_service.py index 8a2539465..17f0d2fb7 100644 --- a/pay-api/tests/unit/services/test_direct_pay_service.py +++ b/pay-api/tests/unit/services/test_direct_pay_service.py @@ -180,7 +180,7 @@ def test_get_receipt(session, public_user_mock): assert rcpt is not None -def test_process_cfs_refund_success(monkeypatch): +def test_process_cfs_refund_success(session, monkeypatch): """Assert refund is successful, when providing a PAID invoice, receipt, a COMPLETED invoice reference.""" payment_account = factory_payment_account() invoice = factory_invoice(payment_account) @@ -199,7 +199,7 @@ def test_process_cfs_refund_success(monkeypatch): assert True -def test_process_cfs_refund_bad_request(): +def test_process_cfs_refund_bad_request(session): """ Assert refund is rejected, only PAID and UPDATE_REVENUE_ACCOUNT are allowed. @@ -215,7 +215,7 @@ def test_process_cfs_refund_bad_request(): assert excinfo.value.code == Error.INVALID_REQUEST.name -def test_process_cfs_refund_duplicate_refund(monkeypatch): +def test_process_cfs_refund_duplicate_refund(session, monkeypatch): """ Assert duplicate refund throws an exception. @@ -325,7 +325,7 @@ def _automated_refund_preparation(): refund_amount=3, refund_type=RefundsPartialType.SERVICE_FEES.value)]), ]) -def test_build_automated_refund_payload_validation(test_name, refund_partial): +def test_build_automated_refund_payload_validation(session, test_name, refund_partial): """Assert validations are working correctly for building refund payload.""" invoice, payment_line_item = _automated_refund_preparation() if test_name == 'pay_pli_not_exist': @@ -344,7 +344,7 @@ def test_build_automated_refund_payload_validation(test_name, refund_partial): ('paybc_amount_too_high', True), ('paybc_already_refunded', True), ]) -def test_build_automated_refund_payload_paybc_validation(test_name, has_exception): +def test_build_automated_refund_payload_paybc_validation(session, test_name, has_exception): """Assert refund payload building works correctly with various PAYBC responses.""" invoice, payment_line_item = _automated_refund_preparation() refund_partial = [ diff --git a/pay-api/tests/unit/services/test_payment_transaction.py b/pay-api/tests/unit/services/test_payment_transaction.py index de8ea794e..2f6387be3 100644 --- a/pay-api/tests/unit/services/test_payment_transaction.py +++ b/pay-api/tests/unit/services/test_payment_transaction.py @@ -617,7 +617,6 @@ def get_receipt(cls, payment_account, pay_response_url: str, return '1234567890', datetime.now(), 100.00 monkeypatch.setattr('pay_api.services.paybc_service.PaybcService.get_receipt', get_receipt) - txn = PaymentTransactionService.create_transaction_for_payment(payment_2.id, get_paybc_transaction_request()) txn = PaymentTransactionService.update_transaction(txn.id, pay_response_url='receipt_number=123451') diff --git a/pay-api/tests/unit/services/test_statement.py b/pay-api/tests/unit/services/test_statement.py index f295c538d..5700ed97f 100644 --- a/pay-api/tests/unit/services/test_statement.py +++ b/pay-api/tests/unit/services/test_statement.py @@ -16,10 +16,11 @@ Test-Suite to ensure that the Statement Service is working as expected. """ -from datetime import datetime, timezone +from datetime import datetime import pytz from freezegun import freeze_time +from sqlalchemy import text from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Statement as StatementModel @@ -31,7 +32,8 @@ from tests.utilities.base_test import ( factory_invoice, factory_invoice_reference, factory_payment, factory_payment_line_item, factory_premium_payment_account, factory_statement, factory_statement_invoices, factory_statement_settings, - get_auth_premium_user, get_eft_enable_account_payload, get_premium_account_payload) + get_auth_premium_user, get_basic_account_payload, get_eft_enable_account_payload, get_premium_account_payload, + get_unlinked_pad_account_payload) def test_statement_find_by_account(session): @@ -172,8 +174,6 @@ def test_get_weekly_interim_statement(session, admin_users_mock): total=50).save() assert weekly_invoice is not None - assert weekly_invoice.created_on == invoice_create_date.astimezone(timezone.utc).replace(tzinfo=None) - update_date = localize_date(datetime(2023, 10, 12, 12, 0)) with freeze_time(update_date): account = PaymentAccountService.update(account.auth_account_id, @@ -232,7 +232,6 @@ def test_get_monthly_interim_statement(session, admin_users_mock): total=50).save() assert monthly_invoice is not None - assert monthly_invoice.created_on == invoice_create_date.astimezone(timezone.utc).replace(tzinfo=None) update_date = localize_date(datetime(2023, 10, 12, 12, 0)) with freeze_time(update_date): @@ -266,3 +265,38 @@ def localize_date(date: datetime): """Localize date object by adding timezone information.""" pst = pytz.timezone('America/Vancouver') return pst.localize(date) + + +def test_statement_various_payment_methods_history(db, app): + """Unit test to test various payment methods over the life of a statement.""" + # We aren't using the session fixture here because we want to test the history_cls + # history_cls wont work on scoped session flush. + with app.app_context(): + db.session.commit = db.session.flush + account: PaymentAccountService = PaymentAccountService.create( + get_premium_account_payload(payment_method=PaymentMethod.DRAWDOWN.value)) + + statement_settings: StatementSettingsModel = StatementSettingsModel \ + .find_active_settings(str(account.auth_account_id), datetime.today()) + + statement = StatementModel( + statement_settings_id=statement_settings.id, + payment_account_id=account.id, + created_on=datetime.today(), + from_date=datetime(2024, 1, 2, 12, 0).date(), + to_date=datetime(2024, 1, 7, 12, 0).date() + ).flush() + + account = PaymentAccountService.update(account.auth_account_id, get_unlinked_pad_account_payload()) + # History row wont be generated for this line: + account = PaymentAccountService.update(account.auth_account_id, get_basic_account_payload()) + # Freezegun, pytest-freezegun don't work here. + db.session.execute(text("update payment_accounts_history set changed = '2024-01-02' " + f"where payment_method = '{PaymentMethod.DRAWDOWN.value}'")) + db.session.execute(text("update payment_accounts_history set changed = '2024-01-03' " + f"where payment_method = '{PaymentMethod.PAD.value}'")) + + payment_methods = statement.payment_methods + assert 'DIRECT_PAY' in payment_methods + assert 'DRAWDOWN' in payment_methods + assert 'PAD' in payment_methods diff --git a/pay-api/tests/utilities/base_test.py b/pay-api/tests/utilities/base_test.py index 46c7aeffa..adf30be69 100644 --- a/pay-api/tests/utilities/base_test.py +++ b/pay-api/tests/utilities/base_test.py @@ -17,7 +17,7 @@ Test-Suite to ensure that the /payments endpoint is working as expected. """ -from datetime import datetime +from datetime import datetime, timezone from decimal import Decimal from random import randrange from typing import Dict, List, Tuple @@ -25,13 +25,13 @@ from faker import Faker from pay_api.models import ( - CfsAccount, Comment, DistributionCode, DistributionCodeLink, EFTFile, EFTShortnames, Invoice, InvoiceReference, - NonSufficientFundsModel, Payment, PaymentAccount, PaymentLineItem, PaymentTransaction, Receipt, RoutingSlip, - Statement, StatementInvoices, StatementSettings) + CfsAccount, Comment, DistributionCode, DistributionCodeLink, EFTFile, EFTShortnameLinks, EFTShortnames, Invoice, + InvoiceReference, NonSufficientFundsModel, Payment, PaymentAccount, PaymentLineItem, PaymentTransaction, Receipt, + RoutingSlip, Statement, StatementInvoices, StatementSettings) from pay_api.utils.constants import DT_SHORT_FORMAT from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, PaymentMethod, PaymentStatus, - PaymentSystem, Role, RoutingSlipStatus) + CfsAccountStatus, EFTShortnameStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, PaymentMethod, + PaymentStatus, PaymentSystem, Role, RoutingSlipStatus) token_header = { @@ -387,7 +387,7 @@ def factory_payment_account(payment_system_code: str = 'PAYBC', payment_method_c name=name, branch_name=branch_name, payment_method=payment_method_code, - pad_activation_date=datetime.now(), + pad_activation_date=datetime.now(tz=timezone.utc), eft_enable=False ).save() @@ -463,7 +463,7 @@ def factory_routing_slip( status: str = RoutingSlipStatus.ACTIVE.value, total: int = 0, remaining_amount: Decimal = 0.0, - routing_slip_date=datetime.now() + routing_slip_date=datetime.now(tz=timezone.utc) ): """Return Factory.""" routing_slip: RoutingSlip = RoutingSlip( @@ -471,7 +471,7 @@ def factory_routing_slip( payment_account_id=payment_account_id, status=status, total=total, - remaining_amount=remaining_amount, + remaining_amount=Decimal(str(remaining_amount)), created_by='test', routing_slip_date=routing_slip_date ) @@ -484,7 +484,7 @@ def factory_routing_slip_usd( status: str = RoutingSlipStatus.ACTIVE.value, total: int = 0, remaining_amount: int = 0, - routing_slip_date=datetime.now(), + routing_slip_date=datetime.now(tz=timezone.utc), total_usd=0 ): """Return Factory.""" @@ -493,7 +493,7 @@ def factory_routing_slip_usd( payment_account_id=payment_account_id, status=status, total=total, - remaining_amount=remaining_amount, + remaining_amount=Decimal(str(remaining_amount)), created_by='test', routing_slip_date=routing_slip_date, total_usd=total_usd @@ -508,7 +508,7 @@ def factory_invoice(payment_account, status_code: str = InvoiceStatus.CREATED.va total=0, paid=None, payment_method_code: str = PaymentMethod.DIRECT_PAY.value, - created_on: datetime = datetime.now(), + created_on: datetime = datetime.now(tz=timezone.utc), routing_slip=None, folio_number=1234567890, created_name='test name', @@ -554,8 +554,8 @@ def factory_payment_transaction( status_code: str = 'CREATED', client_system_url: str = 'http://google.com/', pay_system_url: str = 'http://google.com', - transaction_start_time: datetime = datetime.now(), - transaction_end_time: datetime = datetime.now(), + transaction_start_time: datetime = datetime.now(tz=timezone.utc), + transaction_end_time: datetime = datetime.now(tz=timezone.utc), ): """Return Factory.""" return PaymentTransaction( @@ -578,7 +578,7 @@ def factory_invoice_reference(invoice_id: int, invoice_number: str = '10021'): def factory_receipt( invoice_id: int, receipt_number: str = 'TEST1234567890', - receipt_date: datetime = datetime.now(), + receipt_date: datetime = datetime.now(tz=timezone.utc), receipt_amount: float = 10.0 ): """Return Factory.""" @@ -593,7 +593,7 @@ def factory_receipt( def factory_statement_settings( frequency: str = 'WEEKLY', payment_account_id: str = None, - from_date: datetime = datetime.now(), + from_date: datetime = datetime.now(tz=timezone.utc), to_date: datetime = None): """Return Factory.""" return StatementSettings(frequency=frequency, @@ -605,10 +605,10 @@ def factory_statement_settings( def factory_statement( frequency: str = 'WEEKLY', payment_account_id: str = None, - from_date: datetime = datetime.now(), - to_date: datetime = datetime.now(), + from_date: datetime = datetime.now(tz=timezone.utc), + to_date: datetime = datetime.now(tz=timezone.utc), statement_settings_id: str = None, - created_on: datetime = datetime.now()): + created_on: datetime = datetime.now(tz=timezone.utc)): """Return Factory.""" return Statement(frequency=frequency, statement_settings_id=statement_settings_id, @@ -629,7 +629,7 @@ def factory_statement_invoices( def activate_pad_account(auth_account_id: str): """Activate the pad account.""" payment_account: PaymentAccount = PaymentAccount.find_by_auth_account_id(auth_account_id) - payment_account.pad_activation_date = datetime.now() + payment_account.pad_activation_date = datetime.now(tz=timezone.utc) payment_account.save() cfs_account: CfsAccount = CfsAccount.find_effective_by_account_id(payment_account.id) cfs_account.status = 'ACTIVE' @@ -860,7 +860,7 @@ def get_routing_slip_request( """Return a routing slip request dictionary.""" routing_slip_payload: Dict[str, any] = { 'number': number, - 'routingSlipDate': datetime.now().strftime(DT_SHORT_FORMAT), + 'routingSlipDate': datetime.now(tz=timezone.utc).strftime(DT_SHORT_FORMAT), 'paymentAccount': { 'accountName': 'TEST' }, @@ -869,7 +869,7 @@ def get_routing_slip_request( for cheque_detail in cheque_receipt_numbers: routing_slip_payload['payments'].append({ 'paymentMethod': cheque_detail[1], - 'paymentDate': datetime.now().strftime(DT_SHORT_FORMAT), + 'paymentDate': datetime.now(tz=timezone.utc).strftime(DT_SHORT_FORMAT), 'chequeReceiptNumber': cheque_detail[0], 'paidAmount': cheque_detail[2] }) @@ -891,9 +891,22 @@ def factory_eft_file(file_ref: str = 'test_ref.txt'): return EFTFile(file_ref=file_ref).save() -def factory_eft_shortname(short_name: str, auth_account_id: str = None): +def factory_eft_shortname(short_name: str): """Return an EFT short name model.""" - return EFTShortnames(short_name=short_name, auth_account_id=auth_account_id) + return EFTShortnames(short_name=short_name) + + +def factory_eft_shortname_link(short_name_id: int, auth_account_id: str = '1234', + updated_by: str = None, updated_on: datetime = datetime.now()): + """Return an EFT short name link model.""" + return EFTShortnameLinks( + eft_short_name_id=short_name_id, + auth_account_id=auth_account_id, + status_code=EFTShortnameStatus.PENDING.value, + updated_by=updated_by, + updated_by_name=updated_by, + updated_on=updated_on + ) def factory_non_sufficient_funds(invoice_id: int, invoice_number: str, description: str = None): diff --git a/pay-api/wsgi.py b/pay-api/wsgi.py index ea9964dc3..7c25417b6 100755 --- a/pay-api/wsgi.py +++ b/pay-api/wsgi.py @@ -11,13 +11,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Provides the WSGI entry point for running the application -""" -from pay_api import create_app +"""Provides the WSGI entry point for running the application.""" +from pay_api import create_app -# Openshift s2i expects a lower case name of application -application = create_app() # pylint: disable=invalid-name +app = create_app() # pylint: disable=invalid-name -if __name__ == "__main__": - application.run() +if __name__ == '__main__': + app.run() diff --git a/pay-queue/Dockerfile b/pay-queue/Dockerfile index d290d638e..e402d1323 100644 --- a/pay-queue/Dockerfile +++ b/pay-queue/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye as development_build ARG VCS_REF="missing" ARG BUILD_DATE="missing" @@ -11,26 +11,77 @@ LABEL org.label-schema.vcs-ref=${VCS_REF} \ USER root -# Create working directory -RUN mkdir /opt/app-root && chmod 755 /opt/app-root -WORKDIR /opt/app-root +LABEL maintainer="travissemple" +LABEL vendor="BCROS" -# Install the requirements -COPY ./requirements.txt . +ARG APP_ENV \ + # Needed for fixing permissions of files created by Docker: + UID=1000 \ + GID=1000 -#RUN pip install --upgrade pip -RUN pip install --upgrade pip -RUN pip install --no-cache-dir -r requirements.txt +ENV APP_ENV=${APP_ENV} \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_DEFAULT_TIMEOUT=100 \ + PIP_ROOT_USER_ACTION=ignore \ + # poetry: + POETRY_VERSION=1.8.3 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + POETRY_HOME='/usr/local' -COPY . . +SHELL ["/bin/bash", "-eo", "pipefail", "-c"] -RUN pip install . +RUN apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y \ + bash \ + brotli \ + build-essential \ + curl \ + gettext \ + git \ + libpq-dev \ + wait-for-it \ + && curl -sSL 'https://install.python-poetry.org' | python - \ + && poetry --version \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* -USER 1001 +WORKDIR /code -# Set Python path -ENV PYTHONPATH=/opt/app-root/src +RUN groupadd -g "${GID}" -r web \ + && useradd -d '/code' -g web -l -r -u "${UID}" web \ + && chown web:web -R '/code' -#EXPOSE 5001 +# Copy only requirements, to cache them in docker layer +COPY --chown=web:web ./poetry.lock ./pyproject.toml /code/ -CMD ["gunicorn", "-b 0.0.0.0:8080", "app:app"] +COPY --chown=web:web ./src /code/src +COPY --chown=web:web ./README.md /code + +# Project initialization: +RUN --mount=type=cache,target="$POETRY_CACHE_DIR" \ + echo "$APP_ENV" \ + && poetry version \ + # Install deps: + && poetry run pip install -U pip \ + && poetry install \ + $(if [ -z ${APP_ENV+x} ] | [ "$APP_ENV" = 'production' ]; then echo '--only main'; fi) \ + --no-interaction --no-ansi + +# Running as non-root user: +USER web + +# The following stage is only for production: +FROM development_build AS production_build +COPY --chown=web:web . /code + +CMD [ "python", "/code/app.py" ] diff --git a/pay-queue/Makefile b/pay-queue/Makefile index 9dd13070c..55b54a7d7 100644 --- a/pay-queue/Makefile +++ b/pay-queue/Makefile @@ -12,7 +12,7 @@ DOCKER_NAME:=pay-queue ################################################################################# # COMMANDS -- Setup # ################################################################################# -setup: install install-dev ## Setup the project +setup: install ## Setup the project clean: clean-build clean-pyc clean-test ## Clean the project rm -rf venv/ @@ -36,24 +36,9 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -build-req: clean ## Upgrade requirements - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements/prod.txt ;\ - pip install -Ur requirements/bcregistry-libraries.txt ;\ - pip freeze | sort > requirements.txt ;\ - -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements.txt - -install-dev: ## Install local application - . venv/bin/activate ; \ - pip install -Ur requirements/dev.txt; \ - pip install -e . +install: clean + pip install poetry ;\ + poetry install ################################################################################# # COMMANDS - CI # @@ -61,15 +46,15 @@ install-dev: ## Install local application ci: lint flake8 test ## CI flow pylint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg src/$(PROJECT_NAME) + poetry run pylint --rcfile=setup.cfg src/$(PROJECT_NAME) flake8: ## Linting with flake8 - . venv/bin/activate && flake8 src/$(PROJECT_NAME) tests + poetry run flake8 src/$(PROJECT_NAME) tests lint: pylint flake8 ## run all lint type scripts test: ## Unit testing - . venv/bin/activate && pytest + poetry run pytest mac-cov: test ## Run the coverage report and display in a browser window (mac) @open -a "Google Chrome" htmlcov/index.html @@ -130,7 +115,7 @@ tag: push ## tag image ################################################################################# run: ## Run the project in local - . venv/bin/activate && python app.py -m flask run -p 5001 + poetry run flask run -p 5001 ################################################################################# # Self Documenting Commands # diff --git a/pay-queue/app.py b/pay-queue/app.py old mode 100644 new mode 100755 diff --git a/pay-queue/devops/gcp/clouddeploy-targets.yaml b/pay-queue/devops/gcp/clouddeploy-targets.yaml new file mode 100644 index 000000000..580546cf0 --- /dev/null +++ b/pay-queue/devops/gcp/clouddeploy-targets.yaml @@ -0,0 +1,104 @@ +# Copyright 2022 Google LLCpay-queue +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: dev +description: Dev Environment +deployParameters: + deploy-env: "development" + deploy-project-id: "gtksf3-dev" + service-name: "pay-queue-dev" + container-name: "pay-queue-dev" + app-env: "dev" + cloudsql-instances: "gtksf3-dev:northamerica-northeast1:pay-db-dev" + service-account: "sa-api@gtksf3-dev.iam.gserviceaccount.com" +run: + location: projects/gtksf3-dev/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: test +description: Test Environment +deployParameters: + deploy-env: "development" + deploy-project-id: "gtksf3-test" + service-name: "pay-queue-test" + container-name: "pay-queue-test" + app-env: "test" + cloudsql-instances: "gtksf3-test:northamerica-northeast1:pay-db-test" + service-account: "sa-api@gtksf3-test.iam.gserviceaccount.com" +run: + location: projects/gtksf3-test/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: sandbox +description: Sandbox Environment +requireApproval: true +deployParameters: + deploy-env: "production" + deploy-project-id: "gtksf3-tools" + service-name: "pay-queue-sandbox" + container-name: "pay-queue-sandbox" + app-env: "sandbox" + cloudsql-instances: "gtksf3-tools:northamerica-northeast1:pay-db-sandbox" + service-account: "sa-api@gtksf3-tools.iam.gserviceaccount.com" + max-scale: "50" + container-concurrency: "20" + container-port: "8080" + resources-cpu: 4000m + resources-memory: 8Gi +run: + location: projects/gtksf3-tools/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: prod +description: Production Environment +requireApproval: true +deployParameters: + deploy-env: "production" + deploy-project-id: "gtksf3-prod" + service-name: "pay-queue-prod" + container-name: "pay-queue-prod" + app-env: "production" + cloudsql-instances: "gtksf3-prod:northamerica-northeast1:pay-db-prod" + service-account: "sa-api@gtksf3-prod.iam.gserviceaccount.com" + max-scale: "50" + container-concurrency: "20" + container-port: "8080" + resources-cpu: 4000m + resources-memory: 8Gi +run: + location: projects/gtksf3-prod/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' \ No newline at end of file diff --git a/pay-queue/openshift/templates/pay-queue-build.json b/pay-queue/openshift/templates/pay-queue-build.json deleted file mode 100755 index 444ffd52a..000000000 --- a/pay-queue/openshift/templates/pay-queue-build.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Build template for a pay queue.", - "tags": "flask", - "iconClass": "icon-python" - }, - "name": "${NAME}-build" - }, - "objects": [ - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}" - } - }, - { - "kind": "BuildConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}", - "labels": { - "app": "${NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-build" - } - }, - "spec": { - "source": { - "type": "Git", - "git": { - "uri": "${GIT_REPO_URL}", - "ref": "${GIT_REF}" - }, - "contextDir": "${SOURCE_CONTEXT_DIR}" - }, - "strategy": { - "type": "Docker", - "dockerStrategy": { - "dockerfilePath": "${DOCKER_FILE_PATH}" - } - }, - "output": { - "to": { - "kind": "ImageStreamTag", - "name": "${NAME}:${OUTPUT_IMAGE_TAG}" - } - }, - "triggers": [ - { - "type": "ConfigChange" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the objects defined in this template. You should keep this as default unless your know what your doing.", - "required": true, - "value": "pay_queue" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "GIT_REPO_URL", - "displayName": "Git Repo URL", - "description": "The URL to your GIT repo, don't use the this default unless your just experimenting.", - "required": true, - "value": "https://github.com/bcgov/sbc-pay.git" - }, - { - "name": "GIT_REF", - "displayName": "Git Reference", - "description": "The git reference or branch.", - "required": true, - "value": "development" - }, - { - "name": "SOURCE_CONTEXT_DIR", - "displayName": "Source Context Directory", - "description": "The source context directory.", - "required": true, - "value": "pay_queue" - }, - { - "name": "OUTPUT_IMAGE_TAG", - "displayName": "Output Image Tag", - "description": "The tag given to the built image.", - "required": true, - "value": "latest" - }, - { - "name": "DOCKER_FILE_PATH", - "displayName": "Docker File Path", - "description": "The path to the docker file defining the build.", - "required": false, - "value": "Dockerfile" - } - ] -} diff --git a/pay-queue/openshift/templates/pay-queue-deploy.json b/pay-queue/openshift/templates/pay-queue-deploy.json deleted file mode 100755 index 9c7d2cfc9..000000000 --- a/pay-queue/openshift/templates/pay-queue-deploy.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for pay queue service.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%" - } - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "${IMAGE_NAMESPACE}", - "name": "${NAME}:${TAG_NAME}" - } - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": "${REPLICAS}", - "test": false, - "selector": { - "app": "${NAME}-${TAG_NAME}", - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "template": { - "metadata": { - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "deploymentconfig": "${NAME}-${TAG_NAME}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "containers": [ - { - "name": "${NAME}-${TAG_NAME}", - "image": "docker-registry.default.svc:5000/${IMAGE_NAMESPACE}/${NAME}:${TAG_NAME}", - "ports": [ - { - "containerPort": 5001, - "protocol": "TCP" - } - ], - "env": [ - ], - "resources": { - "requests": { - "cpu": "${CPU_REQUEST}", - "memory": "${MEMORY_REQUEST}" - }, - "limits": { - "cpu": "${CPU_LIMIT}", - "memory": "${MEMORY_LIMIT}" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/healthz", - "port": 7070, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "readinessProbe": { - "httpGet": { - "path": "/readyz", - "port": 7070, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - } - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "pay_queue" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - } - ] -} diff --git a/pay-queue/poetry.lock b/pay-queue/poetry.lock new file mode 100644 index 000000000..f9715fdb2 --- /dev/null +++ b/pay-queue/poetry.lock @@ -0,0 +1,2673 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + +[[package]] +name = "argon2-cffi" +version = "23.1.0" +description = "Argon2 for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, + {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, +] + +[package.dependencies] +argon2-cffi-bindings = "*" + +[package.extras] +dev = ["argon2-cffi[tests,typing]", "tox (>4)"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"] +tests = ["hypothesis", "pytest"] +typing = ["mypy"] + +[[package]] +name = "argon2-cffi-bindings" +version = "21.2.0" +description = "Low-level CFFI bindings for Argon2" +optional = false +python-versions = ">=3.6" +files = [ + {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, + {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, +] + +[package.dependencies] +cffi = ">=1.0.1" + +[package.extras] +dev = ["cogapp", "pre-commit", "pytest", "wheel"] +tests = ["pytest"] + +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + +[[package]] +name = "astroid" +version = "3.2.2" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, + {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "autopep8" +version = "2.2.0" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.8" +files = [ + {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"}, + {file = "autopep8-2.2.0.tar.gz", hash = "sha256:d306a0581163ac29908280ad557773a95a9bede072c0fafed6f141f5311f43c1"}, +] + +[package.dependencies] +pycodestyle = ">=2.11.0" + +[[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 = "cachecontrol" +version = "0.14.0" +description = "httplib2 caching for requests" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachecontrol-0.14.0-py3-none-any.whl", hash = "sha256:f5bf3f0620c38db2e5122c0726bdebb0d16869de966ea6a2befe92470b740ea0"}, + {file = "cachecontrol-0.14.0.tar.gz", hash = "sha256:7db1195b41c81f8274a7bbd97c956f44e8348265a1bc7641c37dfebc39f0c938"}, +] + +[package.dependencies] +msgpack = ">=0.5.2,<2.0.0" +requests = ">=2.16.0" + +[package.extras] +dev = ["CacheControl[filecache,redis]", "black", "build", "cherrypy", "furo", "mypy", "pytest", "pytest-cov", "sphinx", "sphinx-copybutton", "tox", "types-redis", "types-requests"] +filecache = ["filelock (>=3.8.0)"] +redis = ["redis (>=2.10.5)"] + +[[package]] +name = "cachelib" +version = "0.9.0" +description = "A collection of cache libraries in the same API interface." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachelib-0.9.0-py3-none-any.whl", hash = "sha256:811ceeb1209d2fe51cd2b62810bd1eccf70feba5c52641532498be5c675493b3"}, + {file = "cachelib-0.9.0.tar.gz", hash = "sha256:38222cc7c1b79a23606de5c2607f4925779e37cdcea1c2ad21b8bae94b5425a5"}, +] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "cattrs" +version = "23.2.3" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"}, + {file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"}, +] + +[package.dependencies] +attrs = ">=23.1.0" + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +orjson = ["orjson (>=3.9.2)"] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.5.3" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "croniter" +version = "2.0.2" +description = "croniter provides iteration for datetime object with cron like format" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "croniter-2.0.2-py2.py3-none-any.whl", hash = "sha256:78bf110a2c7dbbfdd98b926318ae6c64a731a4c637c7befe3685755110834746"}, + {file = "croniter-2.0.2.tar.gz", hash = "sha256:8bff16c9af4ef1fb6f05416973b8f7cb54997c02f2f8365251f9bf1dded91866"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = ">2021.1" + +[[package]] +name = "cryptography" +version = "42.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "dpath" +version = "2.1.6" +description = "Filesystem-like pathing and searching for dictionaries" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dpath-2.1.6-py3-none-any.whl", hash = "sha256:31407395b177ab63ef72e2f6ae268c15e938f2990a8ecf6510f5686c02b6db73"}, + {file = "dpath-2.1.6.tar.gz", hash = "sha256:f1e07c72e8605c6a9e80b64bc8f42714de08a789c7de417e49c3f87a19692e47"}, +] + +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "expiringdict" +version = "1.2.2" +description = "Dictionary with auto-expiring values for caching purposes" +optional = false +python-versions = "*" +files = [ + {file = "expiringdict-1.2.2-py3-none-any.whl", hash = "sha256:09a5d20bc361163e6432a874edd3179676e935eb81b925eccef48d409a8a45e8"}, + {file = "expiringdict-1.2.2.tar.gz", hash = "sha256:300fb92a7e98f15b05cf9a856c1415b3bc4f2e132be07daa326da6414c23ee09"}, +] + +[package.extras] +tests = ["coverage", "coveralls", "dill", "mock", "nose"] + +[[package]] +name = "flake8" +version = "7.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "flake8-blind-except" +version = "0.2.1" +description = "A flake8 extension that checks for blind except: statements" +optional = false +python-versions = "*" +files = [ + {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, +] + +[[package]] +name = "flake8-debugger" +version = "4.1.2" +description = "ipdb/pdb statement checker plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-isort" +version = "6.1.1" +description = "flake8 plugin that integrates isort" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_isort-6.1.1-py3-none-any.whl", hash = "sha256:0fec4dc3a15aefbdbe4012e51d5531a2eb5fa8b981cdfbc882296a59b54ede12"}, + {file = "flake8_isort-6.1.1.tar.gz", hash = "sha256:c1f82f3cf06a80c13e1d09bfae460e9666255d5c780b859f19f8318d420370b3"}, +] + +[package.dependencies] +flake8 = "*" +isort = ">=5.0.0,<6" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "flake8-quotes" +version = "3.4.0" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, +] + +[package.dependencies] +flake8 = "*" +setuptools = "*" + +[[package]] +name = "flask" +version = "3.0.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, +] + +[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 = "flask-caching" +version = "2.3.0" +description = "Adds caching support to Flask applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, + {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, +] + +[package.dependencies] +cachelib = ">=0.9.0,<0.10.0" +Flask = "*" + +[[package]] +name = "flask-cors" +version = "4.0.0" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Cors-4.0.0.tar.gz", hash = "sha256:f268522fcb2f73e2ecdde1ef45e2fd5c71cc48fe03cffb4b441c6d1b40684eb0"}, + {file = "Flask_Cors-4.0.0-py2.py3-none-any.whl", hash = "sha256:bc3492bfd6368d27cfe79c7821df5a8a319e1a6d5eab277a3794be19bdc51783"}, +] + +[package.dependencies] +Flask = ">=0.9" + +[[package]] +name = "flask-jwt-oidc" +version = "0.7.0" +description = "Opinionated flask oidc client" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +cachelib = "0.*" +Flask = ">=2" +python-jose = "^3.3.0" +six = "^1.16.0" + +[package.source] +type = "git" +url = "https://github.com/seeker25/flask-jwt-oidc.git" +reference = "HEAD" +resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" + +[[package]] +name = "flask-marshmallow" +version = "1.2.0" +description = "Flask + marshmallow for beautiful APIs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_marshmallow-1.2.0-py3-none-any.whl", hash = "sha256:ddd2a7c8db5e00a8d56c8ca5f651efae1de7d76b7d821b56ccc2caf09135ad12"}, + {file = "flask_marshmallow-1.2.0.tar.gz", hash = "sha256:d0f79eb9743f0c530a3d9e848503e1f2228e6b35a819c91e913af02e68421805"}, +] + +[package.dependencies] +Flask = ">=2.2" +marshmallow = ">=3.0.0" + +[package.extras] +dev = ["flask-marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["Sphinx (==7.2.6)", "marshmallow-sqlalchemy (>=0.19.0)", "sphinx-issues (==4.0.0)"] +sqlalchemy = ["flask-sqlalchemy (>=3.0.0)", "marshmallow-sqlalchemy (>=0.29.0)"] +tests = ["flask-marshmallow[sqlalchemy]", "pytest"] + +[[package]] +name = "flask-migrate" +version = "4.0.7" +description = "SQLAlchemy database migrations for Flask applications using Alembic." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Migrate-4.0.7.tar.gz", hash = "sha256:dff7dd25113c210b069af280ea713b883f3840c1e3455274745d7355778c8622"}, + {file = "Flask_Migrate-4.0.7-py3-none-any.whl", hash = "sha256:5c532be17e7b43a223b7500d620edae33795df27c75811ddf32560f7d48ec617"}, +] + +[package.dependencies] +alembic = ">=1.9.0" +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" + +[[package]] +name = "flask-moment" +version = "1.0.5" +description = "Formatting of dates and times in Flask templates using moment.js." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Moment-1.0.5.tar.gz", hash = "sha256:33307ecd4af8290b6df6a9828ff55ac0977d0567817f9bc0f69803d22ed2b55c"}, + {file = "Flask_Moment-1.0.5-py3-none-any.whl", hash = "sha256:6e7b3eef89e2137bbbee975405f241a68a44edfa34bf052c92d84364992adca6"}, +] + +[package.dependencies] +Flask = "*" +packaging = ">=14.1" + +[[package]] +name = "flask-opentracing" +version = "1.1.0" +description = "OpenTracing support for Flask applications" +optional = false +python-versions = "*" +files = [ + {file = "Flask-OpenTracing-1.1.0.tar.gz", hash = "sha256:a9a39d367fbe7e9ed9c77b90ac48159c1a3e82982a5abf84d3f4d710d24580ac"}, +] + +[package.dependencies] +Flask = "*" +opentracing = ">=2.0,<3" + +[package.extras] +tests = ["flake8", "flake8-quotes", "mock", "pytest", "pytest-cov"] + +[[package]] +name = "flask-script" +version = "2.0.6" +description = "Scripting support for Flask" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Script-2.0.6.tar.gz", hash = "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65"}, +] + +[package.dependencies] +Flask = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +description = "Add SQLAlchemy support to your Flask application." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[package.dependencies] +flask = ">=2.2.5" +sqlalchemy = ">=2.0.16" + +[[package]] +name = "freezegun" +version = "1.5.1" +description = "Let your Python tests travel through time" +optional = false +python-versions = ">=3.7" +files = [ + {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, + {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + +[[package]] +name = "gcp-queue" +version = "0.3.0" +description = "" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +flask = ">=1" +google-auth = "^2.28.2" +google-cloud-pubsub = "^2.20.2" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-connect-common.git" +reference = "main" +resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" +subdirectory = "python/gcp-queue" + +[[package]] +name = "google-api-core" +version = "2.19.0" +description = "Google API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, + {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +proto-plus = ">=1.22.3,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-auth" +version = "2.29.0" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-cloud-pubsub" +version = "2.21.2" +description = "Google Cloud Pub/Sub API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, + {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<3.0.0dev" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +grpcio = ">=1.51.3,<2.0dev" +grpcio-status = ">=1.33.2" +proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +libcst = ["libcst (>=0.3.10)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.63.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, + {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.13.0" +description = "IAM API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.13.0.tar.gz", hash = "sha256:fad318608b9e093258fbf12529180f400d1c44453698a33509cc6ecf005b294e"}, + {file = "grpc_google_iam_v1-0.13.0-py2.py3-none-any.whl", hash = "sha256:53902e2af7de8df8c1bd91373d9be55b0743ec267a7428ea638db3775becae89"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.64.0" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.8" +files = [ + {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, + {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, + {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, + {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, + {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, + {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, + {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, + {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, + {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, + {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, + {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, + {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, + {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, + {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, + {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, + {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, + {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, + {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, + {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, + {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, + {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.64.0)"] + +[[package]] +name = "grpcio-status" +version = "1.62.2" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, + {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.62.2" +protobuf = ">=4.21.6" + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "holidays" +version = "0.37" +description = "Generate and work with holidays in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "holidays-0.37-py3-none-any.whl", hash = "sha256:5b8ff8c94c06e3b225762d495e51b8e51205d332f8ad092aab809c4bffa8d123"}, + {file = "holidays-0.37.tar.gz", hash = "sha256:712df71a8d97b04554fa1c9208d219fbf174bad2864263bef24c6dcfa1ded6ff"}, +] + +[package.dependencies] +python-dateutil = "*" + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[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 = "jaeger-client" +version = "4.8.0" +description = "Jaeger Python OpenTracing Tracer implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaeger-client-4.8.0.tar.gz", hash = "sha256:3157836edab8e2c209bd2d6ae61113db36f7ee399e66b1dcbb715d87ab49bfe0"}, +] + +[package.dependencies] +opentracing = ">=2.1,<3.0" +threadloop = ">=1,<2" +thrift = "*" +tornado = ">=4.3" + +[package.extras] +tests = ["codecov", "coverage", "flake8", "flake8-quotes", "flake8-typing-imports", "mock", "mypy", "opentracing_instrumentation (>=3,<4)", "prometheus_client (==0.11.0)", "pycurl", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "pytest-localserver", "pytest-timeout", "pytest-tornado", "tchannel (==2.1.0)"] + +[[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 = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "launchdarkly-eventsource" +version = "1.1.1" +description = "LaunchDarkly SSE Client" +optional = false +python-versions = ">=3.7" +files = [ + {file = "launchdarkly_eventsource-1.1.1-py3-none-any.whl", hash = "sha256:3d7e5301bc4b4a744ecdaa10de8bce52c2f3c66a97e9aa10ab11ca81b67fb31b"}, + {file = "launchdarkly_eventsource-1.1.1.tar.gz", hash = "sha256:211791f1267f9b7b0a62a0bb5fc9c5ed1fb4a834440f16be551968dbe772557a"}, +] + +[package.dependencies] +urllib3 = ">=1.26.0,<3" + +[[package]] +name = "launchdarkly-server-sdk" +version = "8.2.1" +description = "LaunchDarkly SDK for Python" +optional = false +python-versions = "*" +files = [ + {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, + {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, +] + +[package.dependencies] +certifi = ">=2018.4.16" +expiringdict = ">=1.1.4" +pyRFC3339 = ">=1.0" +semver = ">=2.10.2" +urllib3 = ">=1.22.0,<3" + +[package.extras] +consul = ["python-consul (>=1.0.1)"] +dynamodb = ["boto3 (>=1.9.71)"] +redis = ["redis (>=2.10.5)"] + +[[package]] +name = "lovely-pytest-docker" +version = "0.3.1" +description = "Pytest testing utilities with docker containers." +optional = false +python-versions = "*" +files = [ + {file = "lovely-pytest-docker-0.3.1.tar.gz", hash = "sha256:4326a180bfd4dd4ad69c2ef3e3643c41075d965f40068488b40204602e6df85e"}, +] + +[package.dependencies] +pytest = "*" +six = "*" + +[[package]] +name = "mako" +version = "1.3.2" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, + {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "marshmallow" +version = "3.21.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-sqlalchemy" +version = "1.0.0" +description = "SQLAlchemy integration with the marshmallow (de)serialization library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow_sqlalchemy-1.0.0-py3-none-any.whl", hash = "sha256:f415d57809e3555b6323356589aba91e36e4470f35953d3a10c755ac5c3307df"}, + {file = "marshmallow_sqlalchemy-1.0.0.tar.gz", hash = "sha256:20a0f2fcdd5bddc86444fa01461f17f9b6a12a8ddd4ca8c9b34fe2f2e35d00a2"}, +] + +[package.dependencies] +marshmallow = ">=3.10.0" +SQLAlchemy = ">=1.4.40,<3.0" + +[package.extras] +dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)"] +tests = ["pytest (<8)", "pytest-lazy-fixture (>=0.6.2)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "minio" +version = "7.2.7" +description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" +optional = false +python-versions = "*" +files = [ + {file = "minio-7.2.7-py3-none-any.whl", hash = "sha256:59d1f255d852fe7104018db75b3bebbd987e538690e680f7c5de835e422de837"}, + {file = "minio-7.2.7.tar.gz", hash = "sha256:473d5d53d79f340f3cd632054d0c82d2f93177ce1af2eac34a235bea55708d98"}, +] + +[package.dependencies] +argon2-cffi = "*" +certifi = "*" +pycryptodome = "*" +typing-extensions = "*" +urllib3 = "*" + +[[package]] +name = "msgpack" +version = "1.0.8" +description = "MessagePack serializer" +optional = false +python-versions = ">=3.8" +files = [ + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, + {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, + {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, + {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, + {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, + {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, + {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, + {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, + {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, + {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, + {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, + {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, +] + +[[package]] +name = "opentracing" +version = "2.4.0" +description = "OpenTracing API for Python. See documentation at http://opentracing.io" +optional = false +python-versions = "*" +files = [ + {file = "opentracing-2.4.0.tar.gz", hash = "sha256:a173117e6ef580d55874734d1fa7ecb6f3655160b8b8974a2a1e98e5ec9c840d"}, +] + +[package.extras] +tests = ["Sphinx", "doubles", "flake8", "flake8-quotes", "gevent", "mock", "pytest", "pytest-cov", "pytest-mock", "six (>=1.10.0,<2.0)", "sphinx_rtd_theme", "tornado"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pay-api" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.12" +files = [] +develop = false + +[package.dependencies] +alembic = "1.13.1" +attrs = "23.2.0" +blinker = "1.7.0" +cachelib = "0.9.0" +cachetools = "5.3.3" +cattrs = "23.2.3" +certifi = "2024.2.2" +cffi = "1.16.0" +charset-normalizer = "3.3.2" +click = "8.1.7" +croniter = "2.0.2" +cryptography = "42.0.5" +dpath = "2.1.6" +ecdsa = "0.18.0" +expiringdict = "1.2.2" +flask = "3.0.2" +flask-caching = "2.3.0" +flask-cors = "4.0.0" +flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} +flask-marshmallow = "1.2.0" +flask-migrate = "4.0.7" +flask-moment = "1.0.5" +flask-script = "2.0.6" +flask-sqlalchemy = "3.1.1" +gcp-queue = {git = "https://github.com/seeker25/sbc-connect-common.git", branch = "main", subdirectory = "python/gcp-queue"} +greenlet = "3.0.3" +gunicorn = "21.2.0" +holidays = "0.37" +idna = "3.6" +itsdangerous = "2.1.2" +jaeger-client = "4.8.0" +jinja2 = "3.1.3" +jsonschema = "4.17.3" +launchdarkly-eventsource = "1.1.1" +launchdarkly-server-sdk = "8.2.1" +mako = "1.3.2" +markupsafe = "2.1.5" +marshmallow = "3.21.1" +marshmallow-sqlalchemy = "1.0.0" +opentracing = "2.4.0" +packaging = "24.0" +pg8000 = "^1.30.5" +proto-plus = "1.23.0" +protobuf = "4.25.3" +psycopg2-binary = "2.9.9" +pyasn1 = "0.5.1" +pyasn1-modules = "0.3.0" +pycparser = "2.21" +pyhumps = "3.8.0" +pyrfc3339 = "1.1" +pyrsistent = "0.20.0" +python-dateutil = "2.9.0.post0" +python-dotenv = "1.0.1" +python-jose = "3.3.0" +pytz = "2024.1" +requests = "2.31.0" +rsa = "4.9" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +semver = "3.0.2" +sentry-sdk = "1.41.0" +six = "1.16.0" +sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} +sqlalchemy = "2.0.28" +sqlalchemy-utils = "0.41.1" +threadloop = "1.0.2" +thrift = "0.16.0" +tornado = "6.4" +typing-extensions = "4.10.0" +urllib3 = "2.2.1" +werkzeug = "3.0.1" + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-pay.git" +reference = "sync-python-to-main" +resolved_reference = "1dae0d28e241a7bcdfc43dcaee1a36e35e77c31c" +subdirectory = "pay-api" + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "pg8000" +version = "1.31.2" +description = "PostgreSQL interface library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, + {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.2" +scramp = ">=1.4.5" + +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "proto-plus" +version = "1.23.0" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.23.0.tar.gz", hash = "sha256:89075171ef11988b3fa157f5dbd8b9cf09d65fffee97e29ce403cd8defba19d2"}, + {file = "proto_plus-1.23.0-py3-none-any.whl", hash = "sha256:a829c79e619e1cf632de091013a4173deed13a55f326ef84f05af6f50ff4c82c"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + +[[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 = "pyasn1" +version = "0.5.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pycountry" +version = "23.12.11" +description = "ISO country, subdivision, language, currency and script definitions and their translations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycountry-23.12.11-py3-none-any.whl", hash = "sha256:2ff91cff4f40ff61086e773d61e72005fe95de4a57bfc765509db05695dc50ab"}, + {file = "pycountry-23.12.11.tar.gz", hash = "sha256:00569d82eaefbc6a490a311bfa84a9c571cff9ddbf8b0a4f4e7b4f868b4ad925"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pycryptodome" +version = "3.20.0" +description = "Cryptographic library for Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e"}, + {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pyhamcrest" +version = "2.1.0" +description = "Hamcrest framework for matcher objects" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyhamcrest-2.1.0-py3-none-any.whl", hash = "sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587"}, + {file = "pyhamcrest-2.1.0.tar.gz", hash = "sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c"}, +] + +[package.extras] +dev = ["black", "doc2dash", "flake8", "pyhamcrest[docs,tests]", "pytest-mypy", "towncrier", "tox", "tox-asdf", "twine"] +docs = ["alabaster (>=0.7,<1.0)", "sphinx (>=4.0,<5.0)"] +tests = ["coverage[toml]", "dataclasses", "mypy (!=0.940)", "pytest (>=5.0)", "pytest-mypy-plugins", "pytest-sugar", "pytest-xdist", "pyyaml", "types-dataclasses", "types-mock"] +tests-numpy = ["numpy", "pyhamcrest[tests]"] + +[[package]] +name = "pyhumps" +version = "3.8.0" +description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" +optional = false +python-versions = "*" +files = [ + {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, + {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, +] + +[[package]] +name = "pylint" +version = "3.2.2" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, + {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, +] + +[package.dependencies] +astroid = ">=3.2.2,<=3.3.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint-flask" +version = "0.6" +description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" +optional = false +python-versions = "*" +files = [ + {file = "pylint-flask-0.6.tar.gz", hash = "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517"}, +] + +[package.dependencies] +pylint-plugin-utils = ">=0.2.1" + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + +[[package]] +name = "pyrfc3339" +version = "1.1" +description = "Generate and parse RFC 3339 timestamps" +optional = false +python-versions = "*" +files = [ + {file = "pyRFC3339-1.1-py2.py3-none-any.whl", hash = "sha256:67196cb83b470709c580bb4738b83165e67c6cc60e1f2e4f286cfcb402a926f4"}, + {file = "pyRFC3339-1.1.tar.gz", hash = "sha256:81b8cbe1519cdb79bed04910dd6fa4e181faf8c88dff1e1b987b5f7ab23a5b1a"}, +] + +[package.dependencies] +pytz = "*" + +[[package]] +name = "pyrsistent" +version = "0.20.0" +description = "Persistent/Functional/Immutable data structures" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, + {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, + {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, + {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, + {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, + {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, + {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, +] + +[[package]] +name = "pytest" +version = "8.2.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, + {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.18.3" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-asyncio-0.18.3.tar.gz", hash = "sha256:7659bdb0a9eb9c6e3ef992eef11a2b3e69697800ad02fb06374a210d85b29f91"}, + {file = "pytest_asyncio-0.18.3-1-py3-none-any.whl", hash = "sha256:16cf40bdf2b4fb7fc8e4b82bd05ce3fbcd454cbf7b92afc445fe299dabb88213"}, + {file = "pytest_asyncio-0.18.3-py3-none-any.whl", hash = "sha256:8fafa6c52161addfd41ee7ab35f11836c5a16ec208f93ee388f752bea3493a84"}, +] + +[package.dependencies] +pytest = ">=6.1.0" + +[package.extras] +testing = ["coverage (==6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (==0.931)", "pytest-trio (>=0.7.0)"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, +] + +[package.dependencies] +pytest = ">=6.2.5" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +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.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "sbc_common_components" +version = "0.0.0" +description = "" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +flask = "*" +flask-jwt-oidc = ">=0.1.5" +Flask-OpenTracing = "1.1.0" +Flask-SQLAlchemy = "*" +jaeger-client = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-common-components.git" +reference = "HEAD" +resolved_reference = "e770b4ab496e044d292500e62bc19a17079a73ec" +subdirectory = "python" + +[[package]] +name = "scramp" +version = "1.4.5" +description = "An implementation of the SCRAM protocol." +optional = false +python-versions = ">=3.8" +files = [ + {file = "scramp-1.4.5-py3-none-any.whl", hash = "sha256:50e37c464fc67f37994e35bee4151e3d8f9320e9c204fca83a5d313c121bbbe7"}, + {file = "scramp-1.4.5.tar.gz", hash = "sha256:be3fbe774ca577a7a658117dca014e5d254d158cecae3dd60332dfe33ce6d78e"}, +] + +[package.dependencies] +asn1crypto = ">=1.5.1" + +[[package]] +name = "semver" +version = "3.0.2" +description = "Python helper for Semantic Versioning (https://semver.org)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "semver-3.0.2-py3-none-any.whl", hash = "sha256:b1ea4686fe70b981f85359eda33199d60c53964284e0cfb4977d243e37cf4bf4"}, + {file = "semver-3.0.2.tar.gz", hash = "sha256:6253adb39c70f6e51afed2fa7152bcd414c411286088fb4b9effb133885ab4cc"}, +] + +[[package]] +name = "sentry-sdk" +version = "1.41.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.41.0.tar.gz", hash = "sha256:4f2d6c43c07925d8cd10dfbd0970ea7cb784f70e79523cca9dbcd72df38e5a46"}, + {file = "sentry_sdk-1.41.0-py2.py3-none-any.whl", hash = "sha256:be4f8f4b29a80b6a3b71f0f31487beb9e296391da20af8504498a328befed53f"}, +] + +[package.dependencies] +blinker = {version = ">=1.1", optional = true, markers = "extra == \"flask\""} +certifi = "*" +flask = {version = ">=0.11", optional = true, markers = "extra == \"flask\""} +markupsafe = {version = "*", optional = true, markers = "extra == \"flask\""} +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "70.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "simple-cloudevent" +version = "0.0.2" +description = "A short description of the project" +optional = false +python-versions = ">=3.8" +files = [] +develop = false + +[package.dependencies] +strict-rfc3339 = "*" + +[package.source] +type = "git" +url = "https://github.com/daxiom/simple-cloudevent.py.git" +reference = "HEAD" +resolved_reference = "447cabb988202206ac69e71177d7cd11b6c0b002" + +[[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 = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sql-versioning" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.10" +files = [] +develop = false + +[package.source] +type = "git" +url = "https://github.com/bcgov/lear.git" +reference = "feature-legal-name" +resolved_reference = "e5a432d1460dc84208465ef35c0c81ab02e66f51" +subdirectory = "python/common/sql-versioning" + +[[package]] +name = "sqlalchemy" +version = "2.0.28" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "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\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlalchemy-utils" +version = "0.41.1" +description = "Various utility functions for SQLAlchemy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "SQLAlchemy-Utils-0.41.1.tar.gz", hash = "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74"}, + {file = "SQLAlchemy_Utils-0.41.1-py3-none-any.whl", hash = "sha256:6c96b0768ea3f15c0dc56b363d386138c562752b84f647fb8d31a2223aaab801"}, +] + +[package.dependencies] +SQLAlchemy = ">=1.3" + +[package.extras] +arrow = ["arrow (>=0.3.4)"] +babel = ["Babel (>=1.3)"] +color = ["colour (>=0.0.4)"] +encrypted = ["cryptography (>=0.6)"] +intervals = ["intervals (>=0.7.1)"] +password = ["passlib (>=1.6,<2.0)"] +pendulum = ["pendulum (>=2.0.5)"] +phone = ["phonenumbers (>=5.9.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +timezone = ["python-dateutil"] +url = ["furl (>=0.4.1)"] + +[[package]] +name = "strict-rfc3339" +version = "0.7" +description = "Strict, simple, lightweight RFC3339 functions" +optional = false +python-versions = "*" +files = [ + {file = "strict-rfc3339-0.7.tar.gz", hash = "sha256:5cad17bedfc3af57b399db0fed32771f18fc54bbd917e85546088607ac5e1277"}, +] + +[[package]] +name = "threadloop" +version = "1.0.2" +description = "Tornado IOLoop Backed Concurrent Futures" +optional = false +python-versions = "*" +files = [ + {file = "threadloop-1.0.2-py2-none-any.whl", hash = "sha256:5c90dbefab6ffbdba26afb4829d2a9df8275d13ac7dc58dccb0e279992679599"}, + {file = "threadloop-1.0.2.tar.gz", hash = "sha256:8b180aac31013de13c2ad5c834819771992d350267bddb854613ae77ef571944"}, +] + +[package.dependencies] +tornado = "*" + +[[package]] +name = "thrift" +version = "0.16.0" +description = "Python bindings for the Apache Thrift RPC system" +optional = false +python-versions = "*" +files = [ + {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, +] + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + +[[package]] +name = "tomlkit" +version = "0.12.5" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[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.12" +content-hash = "91f9fa21463afa27ba2b73d8efcd0b07bac434743792659ba832a588fc331d50" diff --git a/pay-queue/pyproject.toml b/pay-queue/pyproject.toml new file mode 100644 index 000000000..21af5378e --- /dev/null +++ b/pay-queue/pyproject.toml @@ -0,0 +1,53 @@ +[tool.poetry] +name = "pay-queue" +version = "0.1.0" +description = "" +authors = ["Travis Semple "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +flask = "^3.0.2" +jsonschema = "4.17.3" +python-dotenv = "^1.0.1" +sentry-sdk = {extras = ["flask"], version = "^1.41.0"} +pycountry = "^23.12.11" +werkzeug = "^3.0.1" +minio = "^7.2.5" +attrs = "^23.2.0" +sqlalchemy = "^2.0.28" +itsdangerous = "^2.1.2" +jinja2 = "^3.1.3" +protobuf = "4.25.3" +launchdarkly-server-sdk = "^8.2.1" +cachecontrol = "^0.14.0" +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", branch = "sync-python-to-main", subdirectory = "pay-api"} +pg8000 = "^1.30.5" + + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pytest-mock = "^3.12.0" +requests = "^2.31.0" +pyhamcrest = "^2.1.0" +pytest-cov = "^4.1.0" +freezegun = "^1.4.0" +flake8 = "^7.0.0" +flake8-blind-except = "^0.2.1" +flake8-debugger = "^4.1.2" +flake8-docstrings = "^1.7.0" +flake8-isort = "^6.1.1" +flake8-quotes = "^3.4.0" +pep8-naming = "^0.13.3" +autopep8 = "^2.0.4" +coverage = "^7.4.3" +pylint = "^3.1.0" +pylint-flask = "^0.6" +pydocstyle = "^6.3.0" +isort = "^5.13.2" +lovely-pytest-docker = "^0.3.1" +pytest-asyncio = "0.18.3" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/pay-queue/requirements.txt b/pay-queue/requirements.txt deleted file mode 100644 index e35964a92..000000000 --- a/pay-queue/requirements.txt +++ /dev/null @@ -1,90 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git@94986110a7f6c7ba4f57ed8b038101ba7d864a94#egg=sbc_common_components&subdirectory=python --e git+https://github.com/seeker25/sbc-connect-common.git@cf0d1d76e37ecb9b051393f6e9f4a419177ee2b4#egg=gcp_queue&subdirectory=python/gcp-queue --e git+https://github.com/seeker25/sbc-pay.git@67bf51103557104dd093b89e7a3a1cd625b5efd6#egg=pay_api&subdirectory=pay-api -CacheControl==0.14.0 -Flask-Caching==2.3.0 -Flask-Cors==3.0.10 -Flask-Migrate==2.7.0 -Flask-Moment==1.0.5 -Flask-OpenTracing==1.1.0 -Flask-SQLAlchemy==2.5.1 -Flask-Script==2.0.6 -Flask==1.1.2 -Jinja2==3.0.3 -Mako==1.3.5 -MarkupSafe==2.1.5 -SQLAlchemy-Continuum==1.3.15 -SQLAlchemy-Utils==0.41.2 -SQLAlchemy==1.3.24 -Werkzeug==1.0.1 -alembic==1.13.1 -argon2-cffi-bindings==21.2.0 -argon2-cffi==23.1.0 -attrs==23.2.0 -blinker==1.8.2 -cachelib==0.9.0 -cachetools==5.3.3 -cattrs==23.2.3 -certifi==2024.2.2 -cffi==1.16.0 -charset-normalizer==3.3.2 -click==8.1.7 -croniter==2.0.5 -cryptography==42.0.7 -dpath==2.1.6 -ecdsa==0.19.0 -exceptiongroup==1.2.1 -expiringdict==1.2.2 -flask-jwt-oidc==0.3.0 -flask-marshmallow==0.11.0 -google-api-core==2.19.0 -google-auth==2.29.0 -google-cloud-pubsub==2.21.1 -googleapis-common-protos==1.63.0 -grpc-google-iam-v1==0.13.0 -grpcio-status==1.48.2 -grpcio==1.64.0 -gunicorn==22.0.0 -holidays==0.37 -idna==3.7 -importlib-resources==5.13.0 -importlib_metadata==7.1.0 -itsdangerous==2.0.1 -jaeger-client==4.8.0 -jsonschema==4.17.3 -launchdarkly-server-sdk==8.2.1 -marshmallow-sqlalchemy==0.25.0 -marshmallow==3.21.2 -minio==7.2.7 -msgpack==1.0.8 -opentracing==2.4.0 -packaging==24.0 -pkgutil_resolve_name==1.3.10 -proto-plus==1.23.0 -protobuf==3.19.6 -psycopg2-binary==2.9.9 -pyRFC3339==1.1 -pyasn1==0.6.0 -pyasn1_modules==0.4.0 -pycountry==23.12.11 -pycparser==2.22 -pycryptodome==3.20.0 -pyhumps==3.8.0 -pyrsistent==0.20.0 -python-dateutil==2.9.0.post0 -python-dotenv==1.0.1 -python-jose==3.3.0 -pytz==2024.1 -requests==2.32.2 -rsa==4.9 -semver==3.0.2 -sentry-sdk==2.3.1 -# simple-cloudevent @ git+https://github.com/daxiom/simple-cloudevent.py.git@447cabb988202206ac69e71177d7cd11b6c0b002 -six==1.16.0 -strict-rfc3339==0.7 -threadloop==1.0.2 -thrift==0.20.0 -tornado==6.4 -typing_extensions==4.12.0 -urllib3==2.2.1 -zipp==3.19.0 diff --git a/pay-queue/requirements/bcregistry-libraries.txt b/pay-queue/requirements/bcregistry-libraries.txt deleted file mode 100644 index a388c1fce..000000000 --- a/pay-queue/requirements/bcregistry-libraries.txt +++ /dev/null @@ -1,3 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/seeker25/sbc-pay.git@refactor_queues#egg=pay_api&subdirectory=pay-api --e git+https://github.com/seeker25/sbc-connect-common.git@small_tweaks#egg=gcp-queue&subdirectory=python/gcp-queue diff --git a/pay-queue/requirements/dev.txt b/pay-queue/requirements/dev.txt deleted file mode 100755 index 04624060f..000000000 --- a/pay-queue/requirements/dev.txt +++ /dev/null @@ -1,31 +0,0 @@ -# Everything the developer needs in addition to the production requirements --r prod.txt - -# Testing -pytest -pytest-mock -requests -pyhamcrest -pytest-cov -FreezeGun - -# Lint and code style -flake8==5.0.4 -flake8-blind-except -flake8-debugger -flake8-docstrings -flake8-isort -flake8-quotes -pep8-naming -autopep8 -coverage -pylint -pylint-flask -pydocstyle -isort - - -# docker -lovely-pytest-docker -pytest-asyncio==0.18.3 - diff --git a/pay-queue/requirements/prod.txt b/pay-queue/requirements/prod.txt deleted file mode 100644 index d7135f14d..000000000 --- a/pay-queue/requirements/prod.txt +++ /dev/null @@ -1,15 +0,0 @@ -Flask -jsonschema==4.17.3 -python-dotenv -sentry-sdk[flask] -pycountry -Werkzeug<2 -minio -jaeger-client -attrs -sqlalchemy<1.4 -itsdangerous==2.0.1 -Jinja2==3.0.3 -protobuf~=3.19.5 -launchdarkly-server-sdk==8.2.1 -cachecontrol diff --git a/pay-queue/setup.cfg b/pay-queue/setup.cfg index fba223d37..a3d8c7414 100644 --- a/pay-queue/setup.cfg +++ b/pay-queue/setup.cfg @@ -17,7 +17,7 @@ keywords = [options] zip_safe = True -python_requires = >=3.8.5 +python_requires = >=3.12 include_package_data = True packages = find: diff --git a/pay-queue/src/pay_queue/config.py b/pay-queue/src/pay_queue/config.py index 22cfbf43d..1a284c1a5 100644 --- a/pay-queue/src/pay_queue/config.py +++ b/pay-queue/src/pay_queue/config.py @@ -72,7 +72,12 @@ class _Config(): # pylint: disable=too-few-public-methods,protected-access DB_NAME = os.getenv('DATABASE_NAME', '') DB_HOST = os.getenv('DATABASE_HOST', '') DB_PORT = os.getenv('DATABASE_PORT', '5432') - SQLALCHEMY_DATABASE_URI = f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' + if DB_UNIX_SOCKET := os.getenv('DATABASE_UNIX_SOCKET', None): + SQLALCHEMY_DATABASE_URI = ( + f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432' + ) + else: + SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' # Minio configuration values MINIO_ENDPOINT = os.getenv('MINIO_ENDPOINT') @@ -127,7 +132,7 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods DB_PORT = os.getenv('DATABASE_TEST_PORT', '5432') SQLALCHEMY_DATABASE_URI = os.getenv( 'DATABASE_TEST_URL', - default=f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' + default=f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' ) USE_DOCKER_MOCK = os.getenv('USE_DOCKER_MOCK', None) diff --git a/pay-queue/src/pay_queue/logging.conf b/pay-queue/src/pay_queue/logging.conf deleted file mode 100644 index ded5cb81c..000000000 --- a/pay-queue/src/pay_queue/logging.conf +++ /dev/null @@ -1,34 +0,0 @@ -[loggers] -keys=root,api,asyncio - -[handlers] -keys=console - -[formatters] -keys=simple - -[logger_root] -level=DEBUG -handlers=console - -[logger_asyncio] -level=DEBUG -handlers=console -qualname=asyncio -propagate=0 - -[logger_api] -level=DEBUG -handlers=console -qualname=api -propagate=0 - -[handler_console] -class=StreamHandler -level=DEBUG -formatter=simple -args=(sys.stdout,) - -[formatter_simple] -format=%(asctime)s - %(name)s - %(levelname)s in %(module)s:%(filename)s:%(lineno)d - %(funcName)s: %(message)s -datefmt= diff --git a/pay-queue/src/pay_queue/services/cgi_reconciliations.py b/pay-queue/src/pay_queue/services/cgi_reconciliations.py index ca081f54b..7ec308a8d 100644 --- a/pay-queue/src/pay_queue/services/cgi_reconciliations.py +++ b/pay-queue/src/pay_queue/services/cgi_reconciliations.py @@ -20,7 +20,7 @@ from pay_api.models import DistributionCode as DistributionCodeModel from pay_api.models import EjvFile as EjvFileModel from pay_api.models import EjvHeader as EjvHeaderModel -from pay_api.models import EjvInvoiceLink as EjvInvoiceLinkModel +from pay_api.models import EjvLink as EjvLinkModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import InvoiceReference as InvoiceReferenceModel from pay_api.models import Payment as PaymentModel @@ -32,8 +32,8 @@ from pay_api.services import gcp_queue_publisher from pay_api.services.gcp_queue_publisher import QueueMessage from pay_api.utils.enums import ( - DisbursementStatus, EjvFileType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, PaymentSystem, - QueueSources, RoutingSlipStatus) + DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, + PaymentSystem, QueueSources, RoutingSlipStatus) from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message @@ -79,10 +79,12 @@ def _update_acknowledgement(msg: Dict[str, any]): for ejv_header in ejv_headers: ejv_header.disbursement_status_code = DisbursementStatus.ACKNOWLEDGED.value if ejv_file.file_type == EjvFileType.DISBURSEMENT.value: - ejv_links: List[EjvInvoiceLinkModel] = db.session.query(EjvInvoiceLinkModel).filter( - EjvInvoiceLinkModel.ejv_header_id == ejv_header.id).all() + ejv_links: List[EjvLinkModel] = db.session.query(EjvLinkModel) \ + .filter(EjvLinkModel.ejv_header_id == ejv_header.id) \ + .filter(EjvLinkModel.link_type == EJVLinkType.INVOICE.value) \ + .all() for ejv_link in ejv_links: - invoice: InvoiceModel = InvoiceModel.find_by_id(ejv_link.invoice_id) + invoice: InvoiceModel = InvoiceModel.find_by_id(ejv_link.link_id) invoice.disbursement_status_code = DisbursementStatus.ACKNOWLEDGED.value db.session.commit() @@ -177,16 +179,16 @@ def _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number): # # Work around for CAS, they said fix the feedback files. line = _fix_invoice_line(line) invoice_id = int(line[205:315]) - current_app.logger.warning('Invoice id - %s', invoice_id) + current_app.logger.info('Invoice id - %s', invoice_id) invoice: InvoiceModel = InvoiceModel.find_by_id(invoice_id) - invoice_link: EjvInvoiceLinkModel = db.session.query(EjvInvoiceLinkModel).filter( - EjvInvoiceLinkModel.ejv_header_id == ejv_header_model_id).filter( - EjvInvoiceLinkModel.invoice_id == invoice_id).one_or_none() + invoice_link: EjvLinkModel = db.session.query(EjvLinkModel).filter( + EjvLinkModel.ejv_header_id == ejv_header_model_id).filter( + EjvLinkModel.link_id == invoice_id).filter( + EjvLinkModel.link_type == EJVLinkType.INVOICE.value).one_or_none() invoice_return_code = line[315:319] invoice_return_message = line[319:469] # If the JV process failed, then mark the GL code against the invoice to be stopped # for further JV process for the credit GL. - current_app.logger.info('Is Credit or Debit %s - %s', line[104:105], ejv_file.file_type) if line[104:105] == 'C' and ejv_file.file_type == EjvFileType.DISBURSEMENT.value: disbursement_status = _get_disbursement_status(invoice_return_code) @@ -401,10 +403,11 @@ def _process_ap_header_non_gov_disbursement(line, ejv_file: EjvFileModel) -> boo ap_header_return_code = line[414:418] ap_header_error_message = line[418:568] disbursement_status = _get_disbursement_status(ap_header_return_code) - invoice_link = db.session.query(EjvInvoiceLinkModel)\ + invoice_link = db.session.query(EjvLinkModel)\ .join(EjvHeaderModel).join(EjvFileModel)\ .filter(EjvFileModel.id == ejv_file.id)\ - .filter(EjvInvoiceLinkModel.invoice_id == invoice_id)\ + .filter(EjvLinkModel.link_id == invoice_id)\ + .filter(EjvLinkModel.link_type == EJVLinkType.INVOICE.value) \ .one_or_none() invoice_link.disbursement_status_code = disbursement_status invoice_link.message = ap_header_error_message diff --git a/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py index f3884402f..8ff86f2b9 100644 --- a/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py +++ b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py @@ -28,7 +28,7 @@ from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.services.eft_service import EftService as EFTService from pay_api.services.eft_short_names import EFTShortnames -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, InvoiceStatus, PaymentMethod +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, InvoiceStatus, PaymentMethod from sentry_sdk import capture_message from pay_queue.minio import get_object @@ -104,6 +104,10 @@ def reconcile_eft_payments(msg: Dict[str, any]): # pylint: disable=too-many-loc has_eft_transaction_errors = False + # Include only transactions that are eft or has an error - ignore non EFT + eft_transactions = [transaction for transaction in eft_transactions + if transaction.has_errors() or transaction.is_eft] + # Parse EFT Transactions for eft_transaction in eft_transactions: if eft_transaction.has_errors(): # Flag any instance of an error - will indicate file is partially processed @@ -190,14 +194,6 @@ def _process_eft_credits(shortname_balance, eft_file_id): for shortname in shortname_balance.keys(): try: eft_shortname = _get_shortname(shortname) - payment_account_id = None - - # Get payment account if short name is mapped to an auth account - if eft_shortname.auth_account_id is not None: - payment_account: PaymentAccountModel = PaymentAccountModel.\ - find_by_auth_account_id(eft_shortname.auth_account_id) - payment_account_id = payment_account.id - eft_transactions = shortname_balance[shortname]['transactions'] for eft_transaction in eft_transactions: @@ -217,7 +213,6 @@ def _process_eft_credits(shortname_balance, eft_file_id): continue eft_credit_model.eft_file_id = eft_file_id - eft_credit_model.payment_account_id = payment_account_id eft_credit_model.short_name_id = eft_shortname.id eft_credit_model.amount = deposit_amount eft_credit_model.remaining_amount = deposit_amount @@ -237,6 +232,16 @@ def _process_eft_payments(shortname_balance: Dict, eft_file: EFTFileModel) -> bo for shortname in shortname_balance.keys(): # Retrieve or Create shortname mapping eft_shortname_model = _get_shortname(shortname) + shortname_links = EFTShortnames.get_shortname_links(eft_shortname_model.id) + + # Skip short names with no links or multiple links (manual processing by staff) + if not shortname_links['items'] or len(shortname_links['items']) > 1: + continue + + eft_short_link = shortname_links['items'][0] + # Skip if the link is in pending status - this will be officially linked via a scheduled job + if eft_short_link['status_code'] == EFTShortnameStatus.PENDING.value: + continue # No balance to apply - move to next shortname if shortname_balance[shortname]['balance'] <= 0: @@ -244,21 +249,18 @@ def _process_eft_payments(shortname_balance: Dict, eft_file: EFTFileModel) -> bo shortname, eft_file.file_ref) continue - # check if short name is mapped to an auth account - if eft_shortname_model is not None and eft_shortname_model.auth_account_id is not None: - # We have a mapping and can continue processing - try: - auth_account_id = eft_shortname_model.auth_account_id - # Find invoices to be paid - invoices: List[InvoiceModel] = EFTShortnames.get_invoices_owing(auth_account_id) - if invoices is not None: - for invoice in invoices: - _pay_invoice(invoice=invoice, shortname_balance=shortname_balance[shortname]) - - except Exception as e: # NOQA pylint: disable=broad-exception-caught - has_eft_transaction_errors = True - current_app.logger.error(e) - capture_message('EFT Failed to apply balance to invoice.', level='error') + # We have a mapping and can continue processing + try: + auth_account_id = eft_short_link['account_id'] + # Find invoices to be paid + invoices: List[InvoiceModel] = EFTShortnames.get_invoices_owing(auth_account_id) + for invoice in invoices: + _pay_invoice(invoice=invoice, shortname_balance=shortname_balance[shortname]) + + except Exception as e: # NOQA pylint: disable=broad-exception-caught + has_eft_transaction_errors = True + current_app.logger.error(e) + capture_message('EFT Failed to apply balance to invoice.', level='error') return has_eft_transaction_errors @@ -435,12 +437,11 @@ def _pay_invoice(invoice: InvoiceModel, shortname_balance: Dict): # Create the payment record eft_payment_service: EFTService = PaymentSystemFactory.create_from_payment_method(PaymentMethod.EFT.value) - payment, invoice_reference, receipt = eft_payment_service.apply_credit(invoice=invoice, - payment_date=payment_date, - auto_save=False) + payment, receipt = eft_payment_service.apply_credit(invoice=invoice, + payment_date=payment_date, + auto_save=True) db.session.add(payment) - db.session.add(invoice_reference) db.session.add(receipt) # Paid - update the shortname balance diff --git a/pay-queue/src/pay_queue/services/eft/eft_record.py b/pay-queue/src/pay_queue/services/eft/eft_record.py index 4c002754e..89c483f80 100644 --- a/pay-queue/src/pay_queue/services/eft/eft_record.py +++ b/pay-queue/src/pay_queue/services/eft/eft_record.py @@ -24,6 +24,9 @@ class EFTRecord(EFTBase): """Defines the structure of the transaction record of a received EFT file.""" + PAD_DESCRIPTION_PATTERN = 'MISC PAYMENT BCONLINE' + EFT_DESCRIPTION_PATTERN = 'MISC PAYMENT' + ministry_code: str program_code: str location_id: str @@ -39,6 +42,7 @@ class EFTRecord(EFTBase): jv_type: str # I = inter, J = intra; mandatory if JV batch specified jv_number: str # mandatory if JV batch specified transaction_date: datetime # optional + is_eft: bool = False # Used to help with filtering transactions as not all transactions will necessary be EFT def __init__(self, content: str, index: int): """Return an EFT Transaction record.""" @@ -66,7 +70,11 @@ def _process(self): self.ministry_code = self.extract_value(1, 3) self.program_code = self.extract_value(3, 7) - self.deposit_datetime = self.parse_datetime(self.extract_value(7, 15) + self.extract_value(20, 24), + + deposit_time = self.extract_value(20, 24) + deposit_time = '0000' if len(deposit_time) == 0 else deposit_time # default to 0000 if time not provided + + self.deposit_datetime = self.parse_datetime(self.extract_value(7, 15) + deposit_time, EFTError.INVALID_DEPOSIT_DATETIME) self.location_id = self.extract_value(15, 20) self.transaction_sequence = self.extract_value(24, 27) @@ -75,6 +83,7 @@ def _process(self): self.transaction_description = self.extract_value(27, 67) if len(self.transaction_description) == 0: self.add_error(EFTParseError(EFTError.ACCOUNT_SHORTNAME_REQUIRED)) + self.parse_transaction_description() self.deposit_amount = self.parse_decimal(self.extract_value(67, 80), EFTError.INVALID_DEPOSIT_AMOUNT) self.currency = self.get_currency(self.extract_value(80, 82)) @@ -89,3 +98,14 @@ def _process(self): transaction_date = self.extract_value(131, 139) self.transaction_date = None if len(transaction_date) == 0 \ else self.parse_date(transaction_date, EFTError.INVALID_TRANSACTION_DATE) + + def parse_transaction_description(self): + """Determine if the transaction is an EFT and parse it.""" + if not self.transaction_description: + return + + # Check if this a PAD or EFT Transaction - ignore non EFT Transactions + if self.transaction_description.startswith(self.EFT_DESCRIPTION_PATTERN) \ + and not self.transaction_description.startswith(self.PAD_DESCRIPTION_PATTERN): + self.is_eft = True + self.transaction_description = self.transaction_description[len(self.EFT_DESCRIPTION_PATTERN):].strip() diff --git a/pay-queue/src/pay_queue/version.py b/pay-queue/src/pay_queue/version.py index 2c7d5d346..c29b19b02 100644 --- a/pay-queue/src/pay_queue/version.py +++ b/pay-queue/src/pay_queue/version.py @@ -22,4 +22,4 @@ Development release segment: .devN """ -__version__ = '1.0.0' # pylint: disable=invalid-name +__version__ = '2.0.1' # pylint: disable=invalid-name diff --git a/pay-queue/tests/conftest.py b/pay-queue/tests/conftest.py index a3fa9b61a..8c5fdebba 100644 --- a/pay-queue/tests/conftest.py +++ b/pay-queue/tests/conftest.py @@ -14,30 +14,19 @@ """Common setup and fixtures for the pytest suite used by this service.""" import os from concurrent.futures import CancelledError -from contextlib import contextmanager import pytest from flask_migrate import Migrate, upgrade +from google.api_core.exceptions import NotFound +from google.cloud import pubsub from pay_api import db as _db from pay_api.services.gcp_queue import GcpQueue from sqlalchemy import event, text -from sqlalchemy.schema import DropConstraint, MetaData +from sqlalchemy_utils import create_database, database_exists, drop_database from pay_queue import create_app -@contextmanager -def not_raises(exception): - """Corallary to the pytest raises builtin. - - Assures that an exception is NOT thrown. - """ - try: - yield - except exception: # NOQA - raise pytest.fail(f'DID RAISE {exception}') - - @pytest.fixture(scope='session', autouse=True) def app(): """Return a session-wide application configured in TEST mode.""" @@ -52,69 +41,18 @@ def gcp_queue(app, mocker): return GcpQueue(app) -@pytest.fixture(scope='session') +@pytest.fixture(scope='session', autouse=True) def db(app): # pylint: disable=redefined-outer-name, invalid-name - """Return a session-wide initialised database. - - Drops all existing tables - Meta follows Postgres FKs - """ + """Return a session-wide initialised database.""" with app.app_context(): - # Clear out views - view_sql = """SELECT table_name FROM information_schema.views - WHERE table_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(view_sql))]: - try: - sess.execute(text('DROP VIEW public.%s ;' % seq)) - print('DROP VIEW public.%s ' % seq) - except Exception as err: # NOQA pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # Clear out any existing tables - metadata = MetaData(_db.engine) - metadata.reflect() - for table in metadata.tables.values(): - for fk in table.foreign_keys: # pylint: disable=invalid-name - _db.engine.execute(DropConstraint(fk.constraint)) - metadata.drop_all() - _db.drop_all() - - sequence_sql = """SELECT sequence_name FROM information_schema.sequences - WHERE sequence_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(sequence_sql))]: - try: - sess.execute(text('DROP SEQUENCE public.%s ;' % seq)) - print('DROP SEQUENCE public.%s ' % seq) - except Exception as err: # NOQA pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # ############################################ - # There are 2 approaches, an empty database, or the same one that the app will use - # create the tables - # _db.create_all() - # or - # Use Alembic to load all of the DB revisions including supporting lookup data - # This is the path we'll use in legal_api!! - - # even though this isn't referenced directly, it sets up the internal configs that upgrade - import sys - migrations_path = [folder for folder in sys.path if 'pay-api/pay-api' in folder] - if len(migrations_path) > 0: - migrations_path = migrations_path[0].replace('/pay-api/src', '/pay-api/migrations') - # Fix for windows. - else: - migrations_path = os.path.abspath('../../pay-api/migrations') - - Migrate(app, _db, directory=migrations_path) + if database_exists(_db.engine.url): + drop_database(_db.engine.url) + create_database(_db.engine.url) + _db.session().execute(text('SET TIME ZONE "UTC";')) + pay_api_dir = os.path.abspath('.').replace('pay-queue', 'pay-api') + pay_api_dir = os.path.join(pay_api_dir, 'migrations') + Migrate(app, _db, directory=pay_api_dir) upgrade() - return _db @@ -130,47 +68,40 @@ def client(app): # pylint: disable=redefined-outer-name return app.test_client() -@pytest.fixture(scope='session') -def client_ctx(app): # pylint: disable=redefined-outer-name - """Return session-wide Flask test client.""" - with app.test_client() as _client: - yield _client - - -@pytest.fixture(scope='function') -def session(app, db): # pylint: disable=redefined-outer-name, invalid-name +@pytest.fixture(scope='function', autouse=True) +def session(db, app): # pylint: disable=redefined-outer-name, invalid-name """Return a function-scoped session.""" with app.app_context(): - conn = db.engine.connect() - txn = conn.begin() - - options = dict(bind=conn, binds={}) - sess = db.create_scoped_session(options=options) + with db.engine.connect() as conn: + transaction = conn.begin() + sess = db._make_scoped_session(dict(bind=conn)) # pylint: disable=protected-access + # Establish SAVEPOINT (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) + nested = sess.begin_nested() + db.session = sess + db.session.commit = nested.commit + db.session.rollback = nested.rollback + + @event.listens_for(sess, 'after_transaction_end') + def restart_savepoint(sess2, trans): # pylint: disable=unused-variable + nonlocal nested + if trans.nested: + # Handle where test DOESN'T session.commit() + sess2.expire_all() + nested = sess.begin_nested() + # When using a SAVEPOINT via the Session.begin_nested() or Connection.begin_nested() methods, + # the transaction object returned must be used to commit or rollback the SAVEPOINT. + # Calling the Session.commit() or Connection.commit() methods will always commit the + # outermost transaction; this is a SQLAlchemy 2.0 specific behavior that is + # reversed from the 1.x series + db.session = sess + db.session.commit = nested.commit + db.session.rollback = nested.rollback - # establish a SAVEPOINT just before beginning the test - # (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) - sess.begin_nested() - - @event.listens_for(sess(), 'after_transaction_end') - def restart_savepoint(sess2, trans): # pylint: disable=unused-variable - # Detecting whether this is indeed the nested transaction of the test - if trans.nested and not trans._parent.nested: # pylint: disable=protected-access - # Handle where test DOESN'T session.commit(), - sess2.expire_all() - sess.begin_nested() - - db.session = sess - - sql = text('select 1') - sess.execute(sql) - - yield sess - - # Cleanup - sess.remove() - # This instruction rollsback any commit that were executed in the tests. - txn.rollback() - conn.close() + try: + yield db.session + finally: + db.session.remove() + transaction.rollback() @pytest.fixture(scope='session', autouse=True) @@ -180,6 +111,13 @@ def auto(docker_services, app): docker_services.start('minio') docker_services.start('proxy') docker_services.start('paybc') + docker_services.start('pubsub-emulator') + + +@pytest.fixture() +def mock_publish(monkeypatch): + """Mock check_auth.""" + monkeypatch.setattr('pay_api.services.gcp_queue_publisher.publish_to_queue', lambda *args, **kwargs: None) @pytest.fixture(autouse=True) @@ -188,6 +126,37 @@ def mock_queue_auth(mocker): mocker.patch('pay_queue.external.gcp_auth.verify_jwt', return_value='') +@pytest.fixture(scope='session', autouse=True) +def initialize_pubsub(app): + """Initialize pubsub emulator and respective publisher and subscribers.""" + os.environ['PUBSUB_EMULATOR_HOST'] = 'localhost:8085' + project = app.config.get('TEST_GCP_PROJECT_NAME') + topics = app.config.get('TEST_GCP_TOPICS') + push_config = pubsub.types.PushConfig(push_endpoint=app.config.get('TEST_PUSH_ENDPOINT')) + publisher = pubsub.PublisherClient() + subscriber = pubsub.SubscriberClient() + with publisher, subscriber: + for topic in topics: + topic_path = publisher.topic_path(project, topic) + try: + publisher.delete_topic(topic=topic_path) + except NotFound: + pass + publisher.create_topic(name=topic_path) + subscription_path = subscriber.subscription_path(project, f'{topic}_subscription') + try: + subscriber.delete_subscription(subscription=subscription_path) + except NotFound: + pass + subscriber.create_subscription( + request={ + 'name': subscription_path, + 'topic': topic_path, + 'push_config': push_config, + } + ) + + @pytest.fixture(autouse=True) def mock_pub_sub_call(mocker): """Mock pub sub call.""" @@ -202,34 +171,3 @@ def publish(self, *args, **kwargs): raise CancelledError('This is a mock') mocker.patch('google.cloud.pubsub_v1.PublisherClient', PublisherMock) - - -# @pytest.fixture(scope='session', autouse=True) -# def initialize_pubsub(app): -# """Initialize pubsub emulator and respective publisher and subscribers.""" -# os.environ['PUBSUB_EMULATOR_HOST'] = 'localhost:8085' -# project = app.config.get('TEST_GCP_PROJECT_NAME') -# topics = app.config.get('TEST_GCP_TOPICS') -# push_config = pubsub.types.PushConfig(push_endpoint=app.config.get('TEST_PUSH_ENDPOINT')) -# publisher = pubsub.PublisherClient() -# subscriber = pubsub.SubscriberClient() -# with publisher, subscriber: -# for topic in topics: -# topic_path = publisher.topic_path(project, topic) -# try: -# publisher.delete_topic(topic=topic_path) -# except NotFound: -# pass -# publisher.create_topic(name=topic_path) -# subscription_path = subscriber.subscription_path(project, f'{topic}_subscription') -# try: -# subscriber.delete_subscription(subscription=subscription_path) -# except NotFound: -# pass -# subscriber.create_subscription( -# request={ -# 'name': subscription_path, -# 'topic': topic_path, -# 'push_config': push_config, -# } -# ) diff --git a/pay-queue/tests/integration/test_cgi_reconciliations.py b/pay-queue/tests/integration/test_cgi_reconciliations.py index cbb8b35f7..19ac6a16e 100644 --- a/pay-queue/tests/integration/test_cgi_reconciliations.py +++ b/pay-queue/tests/integration/test_cgi_reconciliations.py @@ -21,7 +21,7 @@ from pay_api.models import DistributionCode as DistributionCodeModel from pay_api.models import EjvFile as EjvFileModel from pay_api.models import EjvHeader as EjvHeaderModel -from pay_api.models import EjvInvoiceLink as EjvInvoiceLinkModel +from pay_api.models import EjvLink as EjvLinkModel from pay_api.models import FeeSchedule as FeeScheduleModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import InvoiceReference as InvoiceReferenceModel @@ -33,8 +33,8 @@ from pay_api.models import RoutingSlip as RoutingSlipModel from pay_api.models import db from pay_api.utils.enums import ( - CfsAccountStatus, DisbursementStatus, EjvFileType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, - PaymentStatus, RoutingSlipStatus) + CfsAccountStatus, DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, + PaymentMethod, PaymentStatus, RoutingSlipStatus) from sbc_common_components.utils.enums import QueueMessageTypes from tests.integration.utils import add_file_event_to_queue_and_process @@ -94,8 +94,9 @@ def test_successful_partner_ejv_reconciliations(session, app, client): partner_code=partner_code, payment_account_id=pay_account.id).save() ejv_header_id = ejv_header.id - EjvInvoiceLinkModel( - invoice_id=invoice.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + EjvLinkModel( + link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() ack_file_name = f'ACK.{file_ref}' @@ -105,7 +106,7 @@ def test_successful_partner_ejv_reconciliations(session, app, client): jv_file.close() # Now upload the ACK file to minio and publish message. - upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) + upload_to_minio(str.encode(''), ack_file_name) add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) @@ -208,8 +209,10 @@ def test_failed_partner_ejv_reconciliations(session, app, client): partner_code=partner_code, payment_account_id=pay_account.id).save() ejv_header_id = ejv_header.id - EjvInvoiceLinkModel( - invoice_id=invoice.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + EjvLinkModel( + link_id=invoice.id, + link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() ack_file_name = f'ACK.{file_ref}' @@ -326,8 +329,9 @@ def test_successful_partner_reversal_ejv_reconciliations(session, app, client): partner_code=partner_code, payment_account_id=pay_account.id).save() ejv_header_id = ejv_header.id - EjvInvoiceLinkModel( - invoice_id=invoice.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + EjvLinkModel( + link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() ack_file_name = f'ACK.{file_ref}' @@ -458,9 +462,9 @@ def test_succesful_payment_ejv_reconciliations(session, app, client): ejv_header: EjvHeaderModel = EjvHeaderModel(disbursement_status_code=DisbursementStatus.UPLOADED.value, ejv_file_id=ejv_file.id, payment_account_id=jv_acc.id).save() - EjvInvoiceLinkModel( - invoice_id=inv.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value - ).save() + EjvLinkModel(link_id=inv.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + ).save() inv_total = f'{inv.total:.2f}'.zfill(15) pay_line_amount = f'{line.total:.2f}'.zfill(15) service_fee_amount = f'{line.service_fees:.2f}'.zfill(15) @@ -616,8 +620,9 @@ def test_succesful_payment_reversal_ejv_reconciliations(session, app, client): ejv_header: EjvHeaderModel = EjvHeaderModel(disbursement_status_code=DisbursementStatus.UPLOADED.value, ejv_file_id=ejv_file.id, payment_account_id=jv_acc.id).save() - EjvInvoiceLinkModel( - invoice_id=inv.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + EjvLinkModel( + link_id=inv.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() inv_total = f'{inv.total:.2f}'.zfill(15) pay_line_amount = f'{line.total:.2f}'.zfill(15) @@ -1042,12 +1047,13 @@ def test_successful_ap_disbursement(session, app, client): ejv_header: EjvHeaderModel = EjvHeaderModel(disbursement_status_code=DisbursementStatus.UPLOADED.value, ejv_file_id=ejv_file.id, payment_account_id=account.id).save() - EjvInvoiceLinkModel( - invoice_id=invoice.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + EjvLinkModel( + link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() - EjvInvoiceLinkModel( - invoice_id=refund_invoice.id, ejv_header_id=ejv_header.id, + EjvLinkModel( + link_id=refund_invoice.id, link_type=EJVLinkType.INVOICE.value, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() @@ -1188,12 +1194,13 @@ def test_failure_ap_disbursement(session, app, client): ejv_header: EjvHeaderModel = EjvHeaderModel(disbursement_status_code=DisbursementStatus.UPLOADED.value, ejv_file_id=ejv_file.id, payment_account_id=account.id).save() - EjvInvoiceLinkModel( - invoice_id=invoice.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + EjvLinkModel( + link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() - EjvInvoiceLinkModel( - invoice_id=refund_invoice.id, ejv_header_id=ejv_header.id, + EjvLinkModel( + link_id=refund_invoice.id, link_type=EJVLinkType.INVOICE.value, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() @@ -1288,14 +1295,14 @@ def test_failure_ap_disbursement(session, app, client): invoice_1 = InvoiceModel.find_by_id(invoice_ids[0]) assert invoice_1.disbursement_status_code == DisbursementStatus.COMPLETED.value assert invoice_1.disbursement_date is not None - invoice_link = db.session.query(EjvInvoiceLinkModel)\ - .filter(EjvInvoiceLinkModel.invoice_id == invoice_ids[0])\ + invoice_link = db.session.query(EjvLinkModel)\ + .filter(EjvLinkModel.link_id == invoice_ids[0])\ .one_or_none() assert invoice_link.disbursement_status_code == DisbursementStatus.COMPLETED.value invoice_2 = InvoiceModel.find_by_id(invoice_ids[1]) assert invoice_2.disbursement_status_code == DisbursementStatus.ERRORED.value - invoice_link = db.session.query(EjvInvoiceLinkModel)\ - .filter(EjvInvoiceLinkModel.invoice_id == invoice_ids[1])\ + invoice_link = db.session.query(EjvLinkModel)\ + .filter(EjvLinkModel.link_id == invoice_ids[1])\ .one_or_none() assert invoice_link.disbursement_status_code == DisbursementStatus.ERRORED.value diff --git a/pay-queue/tests/integration/test_eft_reconciliation.py b/pay-queue/tests/integration/test_eft_reconciliation.py index 47b153913..97fb9520c 100644 --- a/pay-queue/tests/integration/test_eft_reconciliation.py +++ b/pay-queue/tests/integration/test_eft_reconciliation.py @@ -19,20 +19,16 @@ from datetime import datetime from typing import List -from flask import current_app from pay_api import db from pay_api.models import EFTCredit as EFTCreditModel from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceLinkModel from pay_api.models import EFTFile as EFTFileModel +from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel from pay_api.models import EFTShortnames as EFTShortnameModel from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import Invoice as InvoiceModel -from pay_api.models import InvoiceReference as InvoiceReferenceModel -from pay_api.models import Payment as PaymentModel from pay_api.models import PaymentAccount as PaymentAccountModel -from pay_api.models import Receipt as ReceiptModel -from pay_api.utils.enums import ( - EFTFileLineType, EFTProcessStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus) +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, PaymentMethod from sbc_common_components.utils.enums import QueueMessageTypes from pay_queue.services.eft.eft_enums import EFTConstants @@ -262,9 +258,13 @@ def test_eft_tdi17_basic_process(session, app, client): assert eft_shortnames is not None assert len(eft_shortnames) == 2 - assert eft_shortnames[0].auth_account_id is None + + short_name_link_1 = EFTShortnameLinksModel.find_by_short_name_id(eft_shortnames[0].id) + short_name_link_2 = EFTShortnameLinksModel.find_by_short_name_id(eft_shortnames[1].id) + + assert not short_name_link_1 assert eft_shortnames[0].short_name == 'ABC123' - assert eft_shortnames[1].auth_account_id is None + assert not short_name_link_2 assert eft_shortnames[1].short_name == 'DEF456' eft_credits: List[EFTCreditModel] = db.session.query(EFTCreditModel).order_by(EFTCreditModel.created_on.asc()).all() @@ -343,79 +343,83 @@ def test_eft_tdi17_process(session, app, client): assert eft_transactions[2].short_name_id is not None eft_shortnames = db.session.query(EFTShortnameModel).all() + short_name_link_1: EFTShortnameLinksModel = EFTShortnameLinksModel.find_by_short_name_id(eft_shortnames[0].id)[0] + short_name_link_2: EFTShortnameLinksModel = EFTShortnameLinksModel.find_by_short_name_id(eft_shortnames[1].id) assert eft_shortnames is not None assert len(eft_shortnames) == 2 - assert eft_shortnames[0].auth_account_id == eft_shortname.auth_account_id + assert short_name_link_1 + assert short_name_link_1.auth_account_id == payment_account.auth_account_id assert eft_shortnames[0].short_name == 'TESTSHORTNAME' - assert eft_shortnames[1].auth_account_id is None + assert not short_name_link_2 assert eft_shortnames[1].short_name == 'ABC123' # NOTE THIS NEEDS TO BE RE-WRITTEN INSIDE OF THE JOB. - today = datetime.now().date() - # Assert Invoice is paid - invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) - expected_amount = 100 - assert invoice is not None - assert invoice.payment_method_code == PaymentMethod.EFT.value - assert invoice.invoice_status_code == InvoiceStatus.PAID.value - assert invoice.payment_date is not None - assert invoice.payment_date.date() == today - assert invoice.paid == expected_amount - assert invoice.total == expected_amount - - receipt: ReceiptModel = ReceiptModel.find_by_invoice_id_and_receipt_number(invoice.id, invoice.id) - assert receipt is not None - assert receipt.receipt_number == str(invoice.id) - assert receipt.receipt_amount == expected_amount - - expected_invoice_number = f'{current_app.config["EFT_INVOICE_PREFIX"]}{invoice.id}' - payment: PaymentModel = PaymentModel.find_payment_for_invoice(invoice.id) - assert payment is not None - assert payment.payment_date.date() == today - assert payment.invoice_number == expected_invoice_number - assert payment.payment_account_id == payment_account.id - assert payment.payment_status_code == PaymentStatus.COMPLETED.value - assert payment.payment_method_code == PaymentMethod.EFT.value - assert payment.invoice_amount == expected_amount - assert payment.paid_amount == expected_amount - - invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel\ - .find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.COMPLETED.value) - - assert invoice_reference is not None - assert invoice_reference.invoice_id == invoice.id - assert invoice_reference.invoice_number == payment.invoice_number - assert invoice_reference.invoice_number == expected_invoice_number - assert invoice_reference.status_code == InvoiceReferenceStatus.COMPLETED.value - - eft_credits: List[EFTCreditModel] = db.session.query(EFTCreditModel).order_by(EFTCreditModel.created_on.asc()).all() - assert eft_credits is not None - assert len(eft_credits) == 3 - assert eft_credits[0].payment_account_id == payment_account.id - assert eft_credits[0].short_name_id == eft_shortnames[0].id - assert eft_credits[0].eft_file_id == eft_file_model.id - assert eft_credits[0].amount == 100.00 - assert eft_credits[0].remaining_amount == 0 - assert eft_credits[0].eft_transaction_id == eft_transactions[0].id - assert eft_credits[1].payment_account_id == payment_account.id - assert eft_credits[1].short_name_id == eft_shortnames[0].id - assert eft_credits[1].eft_file_id == eft_file_model.id - assert eft_credits[1].amount == 50.5 - assert eft_credits[1].remaining_amount == 50.5 - assert eft_credits[1].eft_transaction_id == eft_transactions[1].id - assert eft_credits[2].payment_account_id is None - assert eft_credits[2].short_name_id == eft_shortnames[1].id - assert eft_credits[2].eft_file_id == eft_file_model.id - assert eft_credits[2].amount == 351.5 - assert eft_credits[2].remaining_amount == 351.5 - assert eft_credits[2].eft_transaction_id == eft_transactions[2].id - - eft_credit_invoice_links: List[EFTCreditInvoiceLinkModel] = db.session.query(EFTCreditInvoiceLinkModel).all() - assert eft_credit_invoice_links is not None - assert len(eft_credit_invoice_links) == 1 - assert eft_credit_invoice_links[0].eft_credit_id == eft_credits[0].id - assert eft_credit_invoice_links[0].invoice_id == invoice.id + # today = datetime.now().date() + # # Assert Invoice is paid + # invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) + # expected_amount = 100 + # assert invoice is not None + # assert invoice.payment_method_code == PaymentMethod.EFT.value + # assert invoice.invoice_status_code == InvoiceStatus.PAID.value + # assert invoice.payment_date is not None + # assert invoice.payment_date.date() == today + # assert invoice.paid == expected_amount + # assert invoice.total == expected_amount + + # receipt: ReceiptModel = ReceiptModel.find_by_invoice_id_and_receipt_number(invoice.id, invoice.id) + # assert receipt is not None + # assert receipt.receipt_number == str(invoice.id) + # assert receipt.receipt_amount == expected_amount + + # expected_invoice_number = f'{current_app.config["EFT_INVOICE_PREFIX"]}{invoice.id}' + # payment: PaymentModel = PaymentModel.find_payment_for_invoice(invoice.id) + # assert payment is not None + # assert payment.payment_date.date() == today + # assert payment.invoice_number == expected_invoice_number + # assert payment.payment_account_id == payment_account.id + # assert payment.payment_status_code == PaymentStatus.COMPLETED.value + # assert payment.payment_method_code == PaymentMethod.EFT.value + # assert payment.invoice_amount == expected_amount + # assert payment.paid_amount == expected_amount + + # invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel\ + # .find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) + + # assert invoice_reference is not None + # assert invoice_reference.invoice_id == invoice.id + # assert invoice_reference.invoice_number == payment.invoice_number + # assert invoice_reference.invoice_number == expected_invoice_number + # assert invoice_reference.status_code == InvoiceReferenceStatus.ACTIVE.value + + # eft_credits: List[EFTCreditModel] = db.session.query(EFTCreditModel) \ + # .order_by(EFTCreditModel.created_on.asc()).all() + # assert eft_credits is not None + # assert len(eft_credits) == 3 + # assert eft_credits[0].payment_account_id == payment_account.id + # assert eft_credits[0].short_name_id == eft_shortnames[0].id + # assert eft_credits[0].eft_file_id == eft_file_model.id + # assert eft_credits[0].amount == 100.00 + # assert eft_credits[0].remaining_amount == 0 + # assert eft_credits[0].eft_transaction_id == eft_transactions[0].id + # assert eft_credits[1].payment_account_id == payment_account.id + # assert eft_credits[1].short_name_id == eft_shortnames[0].id + # assert eft_credits[1].eft_file_id == eft_file_model.id + # assert eft_credits[1].amount == 50.5 + # assert eft_credits[1].remaining_amount == 50.5 + # assert eft_credits[1].eft_transaction_id == eft_transactions[1].id + # assert eft_credits[2].payment_account_id is None + # assert eft_credits[2].short_name_id == eft_shortnames[1].id + # assert eft_credits[2].eft_file_id == eft_file_model.id + # assert eft_credits[2].amount == 351.5 + # assert eft_credits[2].remaining_amount == 351.5 + # assert eft_credits[2].eft_transaction_id == eft_transactions[2].id + + # eft_credit_invoice_links: List[EFTCreditInvoiceLinkModel] = db.session.query(EFTCreditInvoiceLinkModel).all() + # assert eft_credit_invoice_links is not None + # assert len(eft_credit_invoice_links) == 1 + # assert eft_credit_invoice_links[0].eft_credit_id == eft_credits[0].id + # assert eft_credit_invoice_links[0].invoice_id == invoice.id def test_eft_tdi17_rerun(session, app, client): @@ -432,7 +436,7 @@ def test_eft_tdi17_rerun(session, app, client): transaction_1 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', program_code='0146', deposit_date='20230810', deposit_time='0000', location_id='85004', transaction_sequence='001', - transaction_description='TESTSHORTNAME', deposit_amount='13500', + transaction_description='MISC PAYMENT TESTSHORTNAME', deposit_amount='13500', currency='', exchange_adj_amount='0', deposit_amount_cad='FAIL', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', transaction_date='') @@ -474,7 +478,7 @@ def test_eft_tdi17_rerun(session, app, client): transaction_1 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', program_code='0146', deposit_date='20230810', deposit_time='0000', location_id='85004', transaction_sequence='001', - transaction_description='TESTSHORTNAME', deposit_amount='13500', + transaction_description='MISC PAYMENT TESTSHORTNAME', deposit_amount='13500', currency='', exchange_adj_amount='0', deposit_amount_cad='13500', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', transaction_date='') @@ -514,59 +518,66 @@ def test_eft_tdi17_rerun(session, app, client): assert eft_transactions[0].deposit_amount_cents == 13500 # NOTE THIS NEEDS TO BE REWRITTEN IN A JOB - today = datetime.now().date() - # Assert Invoice is paid - invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) - expected_amount = 100 - assert invoice is not None - assert invoice.payment_method_code == PaymentMethod.EFT.value - assert invoice.invoice_status_code == InvoiceStatus.PAID.value - assert invoice.payment_date is not None - assert invoice.payment_date.date() == today - assert invoice.paid == expected_amount - assert invoice.total == expected_amount - - receipt: ReceiptModel = ReceiptModel.find_by_invoice_id_and_receipt_number(invoice.id, invoice.id) - assert receipt is not None - assert receipt.receipt_number == str(invoice.id) - assert receipt.receipt_amount == expected_amount - - expected_invoice_number = f'{current_app.config["EFT_INVOICE_PREFIX"]}{invoice.id}' - payment: PaymentModel = PaymentModel.find_payment_for_invoice(invoice.id) - assert payment is not None - assert payment.payment_date.date() == today - assert payment.invoice_number == expected_invoice_number - assert payment.payment_account_id == payment_account.id - assert payment.payment_status_code == PaymentStatus.COMPLETED.value - assert payment.payment_method_code == PaymentMethod.EFT.value - assert payment.invoice_amount == expected_amount - - invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel \ - .find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.COMPLETED.value) - - assert invoice_reference is not None - assert invoice_reference.invoice_id == invoice.id - assert invoice_reference.invoice_number == payment.invoice_number - assert invoice_reference.invoice_number == expected_invoice_number - assert invoice_reference.status_code == InvoiceReferenceStatus.COMPLETED.value - - eft_credits: List[EFTCreditModel] = db.session.query(EFTCreditModel) \ - .order_by(EFTCreditModel.created_on.asc()).all() - assert eft_credits is not None - assert len(eft_credits) == 1 - assert eft_credits[0].payment_account_id == payment_account.id - assert eft_credits[0].short_name_id == eft_shortname.id - assert eft_credits[0].eft_file_id == eft_file_model.id - assert eft_credits[0].amount == 135 - assert eft_credits[0].remaining_amount == 35 - assert eft_credits[0].eft_transaction_id == eft_transactions[0].id + # today = datetime.now().date() + # # Assert Invoice is paid + # invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) + # expected_amount = 100 + # assert invoice is not None + # assert invoice.payment_method_code == PaymentMethod.EFT.value + # assert invoice.invoice_status_code == InvoiceStatus.PAID.value + # assert invoice.payment_date is not None + # assert invoice.payment_date.date() == today + # assert invoice.paid == expected_amount + # assert invoice.total == expected_amount + + # receipt: ReceiptModel = ReceiptModel.find_by_invoice_id_and_receipt_number(invoice.id, invoice.id) + # assert receipt is not None + # assert receipt.receipt_number == str(invoice.id) + # assert receipt.receipt_amount == expected_amount + + # expected_invoice_number = f'{current_app.config["EFT_INVOICE_PREFIX"]}{invoice.id}' + # payment: PaymentModel = PaymentModel.find_payment_for_invoice(invoice.id) + # assert payment is not None + # assert payment.payment_date.date() == today + # assert payment.invoice_number == expected_invoice_number + # assert payment.payment_account_id == payment_account.id + # assert payment.payment_status_code == PaymentStatus.COMPLETED.value + # assert payment.payment_method_code == PaymentMethod.EFT.value + # assert payment.invoice_amount == expected_amount + + # invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel \ + # .find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) + + # assert invoice_reference is not None + # assert invoice_reference.invoice_id == invoice.id + # assert invoice_reference.invoice_number == payment.invoice_number + # assert invoice_reference.invoice_number == expected_invoice_number + # assert invoice_reference.status_code == InvoiceReferenceStatus.ACTIVE.value + + # eft_credits: List[EFTCreditModel] = db.session.query(EFTCreditModel) \ + # .order_by(EFTCreditModel.created_on.asc()).all() + # assert eft_credits is not None + # assert len(eft_credits) == 1 + # assert eft_credits[0].payment_account_id == payment_account.id + # assert eft_credits[0].short_name_id == eft_shortname.id + # assert eft_credits[0].eft_file_id == eft_file_model.id + # assert eft_credits[0].amount == 135 + # assert eft_credits[0].remaining_amount == 35 + # assert eft_credits[0].eft_transaction_id == eft_transactions[0].id def create_test_data(): """Create test seed data.""" payment_account: PaymentAccountModel = factory_create_eft_account() - eft_shortname: EFTShortnameModel = EFTShortnameModel(short_name='TESTSHORTNAME', - auth_account_id=payment_account.auth_account_id).save() + eft_shortname: EFTShortnameModel = EFTShortnameModel(short_name='TESTSHORTNAME').save() + EFTShortnameLinksModel( + eft_short_name_id=eft_shortname.id, + auth_account_id=payment_account.auth_account_id, + status_code=EFTShortnameStatus.LINKED.value, + updated_by='IDIR/JSMITH', + updated_by_name='IDIR/JSMITH', + updated_on=datetime.now() + ).save() invoice: InvoiceModel = factory_invoice(payment_account=payment_account, total=100, service_fees=10.0, payment_method_code=PaymentMethod.EFT.value) @@ -585,21 +596,41 @@ def generate_basic_tdi17_file(file_name: str): transaction_1 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', program_code='0146', deposit_date='20230810', deposit_time='0000', location_id='85004', transaction_sequence='001', - transaction_description='ABC123', deposit_amount='13500', + transaction_description='MISC PAYMENT ABC123', deposit_amount='13500', currency='', exchange_adj_amount='0', deposit_amount_cad='13500', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', transaction_date='') transaction_2 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', - program_code='0146', deposit_date='20230810', deposit_time='0000', + program_code='0146', deposit_date='20230810', deposit_time='', location_id='85004', transaction_sequence='002', - transaction_description='DEF456', + transaction_description='MISC PAYMENT DEF456', deposit_amount='525000', currency='', exchange_adj_amount='0', deposit_amount_cad='525000', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', transaction_date='') - create_and_upload_eft_file(file_name, [header, transaction_1, transaction_2, trailer]) + transaction_3 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', + program_code='0146', deposit_date='20230810', deposit_time='0000', + location_id='85004', transaction_sequence='003', + transaction_description='SHOULDIGNORE', + deposit_amount='525000', currency='', exchange_adj_amount='0', + deposit_amount_cad='525000', destination_bank_number='0003', + batch_number='002400986', jv_type='I', jv_number='002425669', + transaction_date='') + + transaction_4 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', + program_code='0146', deposit_date='20230810', deposit_time='0000', + location_id='85004', transaction_sequence='004', + transaction_description='MISC PAYMENT BCONLINE SHOULDIGNORE', + deposit_amount='525000', currency='', exchange_adj_amount='0', + deposit_amount_cad='525000', destination_bank_number='0003', + batch_number='002400986', jv_type='I', jv_number='002425669', + transaction_date='') + + create_and_upload_eft_file(file_name, [header, + transaction_1, transaction_2, transaction_3, transaction_4, + trailer]) def generate_tdi17_file(file_name: str): @@ -613,15 +644,15 @@ def generate_tdi17_file(file_name: str): transaction_1 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', program_code='0146', deposit_date='20230810', deposit_time='0000', location_id='85004', transaction_sequence='001', - transaction_description='TESTSHORTNAME', deposit_amount='10000', + transaction_description='MISC PAYMENT TESTSHORTNAME', deposit_amount='10000', currency='', exchange_adj_amount='0', deposit_amount_cad='10000', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', transaction_date='') transaction_2 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', - program_code='0146', deposit_date='20230810', deposit_time='0000', + program_code='0146', deposit_date='20230810', deposit_time='', location_id='85004', transaction_sequence='002', - transaction_description='TESTSHORTNAME', + transaction_description='MISC PAYMENT TESTSHORTNAME', deposit_amount='5050', currency='', exchange_adj_amount='0', deposit_amount_cad='5050', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', @@ -630,9 +661,20 @@ def generate_tdi17_file(file_name: str): transaction_3 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', program_code='0146', deposit_date='20230810', deposit_time='0000', location_id='85004', transaction_sequence='003', - transaction_description='ABC123', deposit_amount='35150', + transaction_description='MISC PAYMENT ABC123', deposit_amount='35150', currency='', exchange_adj_amount='0', deposit_amount_cad='35150', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', transaction_date='') - create_and_upload_eft_file(file_name, [header, transaction_1, transaction_2, transaction_3, trailer]) + transaction_4 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', + program_code='0146', deposit_date='20230810', deposit_time='0000', + location_id='85004', transaction_sequence='004', + transaction_description='MISC PAYMENT BCONLINE SHOULDIGNORE', + deposit_amount='525000', currency='', exchange_adj_amount='0', + deposit_amount_cad='525000', destination_bank_number='0003', + batch_number='002400986', jv_type='I', jv_number='002425669', + transaction_date='') + + create_and_upload_eft_file(file_name, [header, + transaction_1, transaction_2, transaction_3, transaction_4, + trailer]) diff --git a/pay-queue/tests/unit/test_eft_file_parser.py b/pay-queue/tests/unit/test_eft_file_parser.py index fd667674f..73d27124e 100644 --- a/pay-queue/tests/unit/test_eft_file_parser.py +++ b/pay-queue/tests/unit/test_eft_file_parser.py @@ -555,6 +555,7 @@ def test_eft_parse_file(): assert eft_records[0].jv_type == 'I' assert eft_records[0].jv_number == '002425669' assert eft_records[0].transaction_date is None + assert not eft_records[0].is_eft assert eft_records[1].index == 2 assert eft_records[1].record_type == '2' @@ -573,6 +574,7 @@ def test_eft_parse_file(): assert eft_records[1].jv_type == 'I' assert eft_records[1].jv_number == '002425669' assert eft_records[1].transaction_date is None + assert not eft_records[1].is_eft assert eft_records[2].index == 3 assert eft_records[2].record_type == '2' @@ -591,6 +593,7 @@ def test_eft_parse_file(): assert eft_records[2].jv_type == 'I' assert eft_records[2].jv_number == '002425669' assert eft_records[2].transaction_date is None + assert not eft_records[2].is_eft assert eft_records[3].index == 4 assert eft_records[3].record_type == '2' @@ -609,6 +612,7 @@ def test_eft_parse_file(): assert eft_records[3].jv_type == 'I' assert eft_records[3].jv_number == '002425669' assert eft_records[3].transaction_date is None + assert not eft_records[3].is_eft assert eft_records[4].index == 5 assert eft_records[4].record_type == '2' @@ -627,3 +631,4 @@ def test_eft_parse_file(): assert eft_records[4].jv_type == 'I' assert eft_records[4].jv_number == '002425836' assert eft_records[4].transaction_date is None + assert not eft_records[4].is_eft diff --git a/pay-queue/tests/utilities/factory_utils.py b/pay-queue/tests/utilities/factory_utils.py index d8047880a..f493b3b88 100644 --- a/pay-queue/tests/utilities/factory_utils.py +++ b/pay-queue/tests/utilities/factory_utils.py @@ -47,7 +47,8 @@ def factory_eft_record(record_type: str, ministry_code: str, program_code: str, exchange_adj_amount = transform_money_string(exchange_adj_amount) deposit_amount_cad = transform_money_string(deposit_amount_cad) - result = f'{record_type}{ministry_code}{program_code}{deposit_date}{location_id}{deposit_time}' \ + result = f'{record_type}{ministry_code}{program_code}{deposit_date}{location_id}' \ + f'{right_pad_space(deposit_time, 4)}' \ f'{transaction_sequence}{right_pad_space(transaction_description, 40)}' \ f'{left_pad_zero(deposit_amount, 13)}{right_pad_space(currency, 2)}' \ f'{left_pad_zero(exchange_adj_amount, 13)}{left_pad_zero(deposit_amount_cad, 13)}' \ diff --git a/report-api/Dockerfile b/report-api/Dockerfile index 757b62c72..9cc892b82 100644 --- a/report-api/Dockerfile +++ b/report-api/Dockerfile @@ -1,27 +1,4 @@ -FROM python:3.8-bullseye -#FROM python:.6-stretch - -#RUN echo "deb http://ftp.debian.org/debian stretch main contrib" > /etc/apt/sources.list - -# todo: Revert this entire pull request when libcairo2 >= 1.14.2 is available from the debian -# jessie repo. This is a temporary fix for https://github.com/Kozea/WeasyPrint/issues/233 - -# reconfigure Debian to allow installs from both stretch (testing) repo and jessie (stable) repo -# install all the dependencies except libcairo2 from jessie, then install libcairo2 from stretch - -#RUN apt-get -y update \ -# && apt-get install -y \ -# fonts-font-awesome \ -# libffi-dev \ -# libgdk-pixbuf2.0-0 \ -# python-dev \ -# python-lxml \ -# shared-mime-info \ -# && apt-get install -y ttf-mscorefonts-installer \ -# libpango1.0-0 \ -# libcairo2 \ -# libpangocairo-1.0-0 \ -# && apt-get -y clean +FROM python:3.12.2-bullseye ARG VCS_REF="missing" ARG BUILD_DATE="missing" @@ -51,5 +28,4 @@ EXPOSE 5001 ENV NUM_WORKERS=3 ENV TIMEOUT=360 -#CMD ["gunicorn", "--bind", "0.0.0.0:5001", "--timeout", "$TIMEOUT", "--workers", "$NUM_WORKERS", "wsgi:app"] CMD gunicorn --bind 0.0.0.0:5001 --timeout $TIMEOUT --workers $NUM_WORKERS wsgi:application diff --git a/report-api/src/api/resources/__init__.py b/report-api/src/api/resources/__init__.py index 9cfc8d130..4fe7e51df 100755 --- a/report-api/src/api/resources/__init__.py +++ b/report-api/src/api/resources/__init__.py @@ -34,7 +34,6 @@ __all__ = ('API_BLUEPRINT', 'OPS_BLUEPRINT') # This will add the Authorize button to the swagger docs -# TODO oauth2 & openid may not yet be supported by restplus <- check on this AUTHORIZATIONS = { 'apikey': { 'type': 'apiKey', diff --git a/report-api/src/api/services/report_service.py b/report-api/src/api/services/report_service.py index c8a84d159..7ffa07809 100644 --- a/report-api/src/api/services/report_service.py +++ b/report-api/src/api/services/report_service.py @@ -27,6 +27,7 @@ def format_datetime(value, format='short'): # pylint: disable=redefined-builtin """Filter to format datetime globally.""" + dt_format = '%m-%d-%Y' if format == 'full': dt_format = '%m-%d-%Y %I:%M %p' elif format == 'short': diff --git a/report-api/src/api/utils/logging.py b/report-api/src/api/utils/logging.py index 8b88ddcf2..afa83de96 100755 --- a/report-api/src/api/utils/logging.py +++ b/report-api/src/api/utils/logging.py @@ -18,14 +18,9 @@ def setup_logging(conf): - """Create the services logger. - - TODO should be reworked to load in the proper loggers and remove others - """ - # log_file_path = path.join(path.abspath(path.dirname(__file__)), conf) - + """Create the services logger.""" if conf and path.isfile(conf): logging.config.fileConfig(conf) - print(f'Configure logging, from conf:{conf}', file=sys.stdout) + print(f'Configure logging, from conf: {conf}', file=sys.stdout) else: - print(f'Unable to configure logging, attempted conf:{conf}', file=sys.stderr) + print(f'Unable to configure logging, attempted conf: {conf}', file=sys.stderr)