From 8d7d92946d5863582be84fe43bea92918d6b6048 Mon Sep 17 00:00:00 2001 From: James Harrison Date: Wed, 8 Nov 2023 12:09:46 +0000 Subject: [PATCH 1/7] Remove AutoFlow --- .circleci/config.yml | 64 +- Makefile | 2 +- autoflow.Dockerfile | 47 - autoflow.Dockerfile.dockerignore | 20 - autoflow/.coveragerc | 24 - autoflow/.gitattributes | 1 - autoflow/Gemfile | 6 - autoflow/Gemfile.lock | 61 - autoflow/MANIFEST.in | 2 - autoflow/Pipfile | 40 - autoflow/Pipfile.lock | 4119 ----------------- autoflow/README.md | 50 - autoflow/autoflow/__init__.py | 8 - autoflow/autoflow/__main__.py | 13 - autoflow/autoflow/_version.py | 692 --- autoflow/autoflow/app.py | 78 - autoflow/autoflow/date_stencil.py | 213 - autoflow/autoflow/model.py | 147 - autoflow/autoflow/parser/__init__.py | 10 - .../parser/available_dates_sensor_schema.py | 40 - autoflow/autoflow/parser/custom_fields.py | 95 - .../autoflow/parser/notebook_output_schema.py | 52 - autoflow/autoflow/parser/notebook_schema.py | 57 - autoflow/autoflow/parser/notebooks_field.py | 68 - autoflow/autoflow/parser/parser.py | 78 - .../autoflow/parser/workflow_config_schema.py | 119 - autoflow/autoflow/parser/workflow_schema.py | 69 - autoflow/autoflow/sensor.py | 345 -- autoflow/autoflow/utils.py | 266 -- autoflow/autoflow/workflows.py | 237 - autoflow/config/asciidoc_extended.tpl | 42 - autoflow/config/config.toml | 15 - autoflow/docker-stack.yml | 51 - autoflow/examples/inputs/flows_report.ipynb | 251 - autoflow/examples/inputs/run_flows.ipynb | 177 - autoflow/examples/inputs/workflows.yml | 34 - autoflow/examples/outputs/.gitignore | 2 - autoflow/pyproject.toml | 3 - autoflow/setup.cfg | 17 - autoflow/setup.py | 50 - autoflow/tests/conftest.py | 81 - .../test_available_dates_sensor_schema.py | 102 - .../tests/schema_tests/test_custom_fields.py | 96 - .../test_notebook_output_schema.py | 78 - .../schema_tests/test_notebook_schema.py | 116 - .../schema_tests/test_notebooks_field.py | 109 - .../test_workflow_config_schema.py | 222 - .../schema_tests/test_workflow_schema.py | 141 - autoflow/tests/test_date_stencil.py | 168 - autoflow/tests/test_model.py | 160 - autoflow/tests/test_parser.py | 141 - autoflow/tests/test_sensor.py | 660 --- autoflow/tests/test_utils.py | 331 -- autoflow/tests/test_workflows.py | 336 -- autoflow/versioneer.py | 2205 --------- development_environment | 22 - docker-compose-build.yml | 6 - docker-compose.autoflow.yml | 25 - docs/source/administrator/deployment.md | 22 - docs/source/analyst/autoflow.md | 184 - docs/source/analyst/index.md | 4 - .../source/developer/dev_environment_setup.md | 16 +- integration_tests/Gemfile | 1 - integration_tests/Gemfile.lock | 1 - integration_tests/Pipfile | 1 - .../tests/autoflow_tests/test_example.py | 82 - 66 files changed, 6 insertions(+), 12969 deletions(-) delete mode 100644 autoflow.Dockerfile delete mode 100644 autoflow.Dockerfile.dockerignore delete mode 100644 autoflow/.coveragerc delete mode 100644 autoflow/.gitattributes delete mode 100644 autoflow/Gemfile delete mode 100644 autoflow/Gemfile.lock delete mode 100644 autoflow/MANIFEST.in delete mode 100644 autoflow/Pipfile delete mode 100644 autoflow/Pipfile.lock delete mode 100644 autoflow/README.md delete mode 100644 autoflow/autoflow/__init__.py delete mode 100644 autoflow/autoflow/__main__.py delete mode 100644 autoflow/autoflow/_version.py delete mode 100644 autoflow/autoflow/app.py delete mode 100644 autoflow/autoflow/date_stencil.py delete mode 100644 autoflow/autoflow/model.py delete mode 100644 autoflow/autoflow/parser/__init__.py delete mode 100644 autoflow/autoflow/parser/available_dates_sensor_schema.py delete mode 100644 autoflow/autoflow/parser/custom_fields.py delete mode 100644 autoflow/autoflow/parser/notebook_output_schema.py delete mode 100644 autoflow/autoflow/parser/notebook_schema.py delete mode 100644 autoflow/autoflow/parser/notebooks_field.py delete mode 100644 autoflow/autoflow/parser/parser.py delete mode 100644 autoflow/autoflow/parser/workflow_config_schema.py delete mode 100644 autoflow/autoflow/parser/workflow_schema.py delete mode 100644 autoflow/autoflow/sensor.py delete mode 100644 autoflow/autoflow/utils.py delete mode 100644 autoflow/autoflow/workflows.py delete mode 100644 autoflow/config/asciidoc_extended.tpl delete mode 100644 autoflow/config/config.toml delete mode 100644 autoflow/docker-stack.yml delete mode 100644 autoflow/examples/inputs/flows_report.ipynb delete mode 100644 autoflow/examples/inputs/run_flows.ipynb delete mode 100644 autoflow/examples/inputs/workflows.yml delete mode 100644 autoflow/examples/outputs/.gitignore delete mode 100644 autoflow/pyproject.toml delete mode 100644 autoflow/setup.cfg delete mode 100644 autoflow/setup.py delete mode 100644 autoflow/tests/conftest.py delete mode 100644 autoflow/tests/schema_tests/test_available_dates_sensor_schema.py delete mode 100644 autoflow/tests/schema_tests/test_custom_fields.py delete mode 100644 autoflow/tests/schema_tests/test_notebook_output_schema.py delete mode 100644 autoflow/tests/schema_tests/test_notebook_schema.py delete mode 100644 autoflow/tests/schema_tests/test_notebooks_field.py delete mode 100644 autoflow/tests/schema_tests/test_workflow_config_schema.py delete mode 100644 autoflow/tests/schema_tests/test_workflow_schema.py delete mode 100644 autoflow/tests/test_date_stencil.py delete mode 100644 autoflow/tests/test_model.py delete mode 100644 autoflow/tests/test_parser.py delete mode 100644 autoflow/tests/test_sensor.py delete mode 100644 autoflow/tests/test_utils.py delete mode 100644 autoflow/tests/test_workflows.py delete mode 100644 autoflow/versioneer.py delete mode 100644 docker-compose.autoflow.yml delete mode 100644 docs/source/analyst/autoflow.md delete mode 120000 integration_tests/Gemfile delete mode 120000 integration_tests/Gemfile.lock delete mode 100644 integration_tests/tests/autoflow_tests/test_example.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 630863544f..eea523f6f7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -637,56 +637,12 @@ jobs: - upload_codecov: test: test_results - run_autoflow_tests: - docker: - - image: cimg/python:3.8.5 - working_directory: /home/circleci/project/autoflow - steps: - - checkout: - path: /home/circleci/project/ - - restore_cache: - keys: - - autoflow-deps-6-{{ checksum "Pipfile.lock" }} - - run: &install_autoflow_deps - name: Install non-python autoflow dependencies - command: | - sudo apt-get update --allow-releaseinfo-change && sudo apt-get install -y postgresql pandoc ruby - sudo gem i rubygems-update -v '<3' - sudo update_rubygems - sudo gem install bundler -v 2.3.26 - sudo gem install bundle - bundle install - - run: - name: Install python dependencies - command: | - pipenv install --deploy --dev - - save_cache: - key: autoflow-deps-6-{{ checksum "Pipfile.lock" }} - paths: - - /home/circleci/.local/share/virtualenvs/ - - run: - name: Run autoflow unit tests - command: | - pipenv run python -m pytest --junitxml=test_results/pytest/results.xml --cov=./ \ - --cov-report term --cov-report xml --durations=10 - - store_test_results: - path: test_results - - upload_codecov: - test: test_results - build_docker_image: parameters: component: type: enum enum: - [ - "flowmachine", - "flowauth", - "flowapi", - "flowkit-examples", - "flowetl", - "autoflow", - ] + ["flowmachine", "flowauth", "flowapi", "flowkit-examples", "flowetl"] component_path: type: string machine: @@ -826,11 +782,6 @@ jobs: at: /home/circleci - restore_cache: key: integration-test-deps-6-{{ checksum "Pipfile.lock" }} - - when: - condition: - equal: ["not query_tests", << parameters.pytest_selector >>] - steps: - - run: *install_autoflow_deps - run: pipenv install --deploy --dev - save_cache: key: integration-test-deps-6-{{ checksum "Pipfile.lock" }} @@ -841,7 +792,7 @@ jobs: name: Run integration tests command: | PIPENV_DONT_LOAD_ENV=1 FLOWAPI_PORT=9090 pipenv run pytest -sv --junitxml=test_results/pytest/results.xml --durations=10 \ - --cov=../flowmachine --cov=../flowclient --cov=../flowapi --cov=../autoflow \ + --cov=../flowmachine --cov=../flowclient --cov=../flowapi \ --cov-report xml \ --cov-report term \ -k "<>" @@ -1052,7 +1003,7 @@ jobs: command: | export DOCKER_USER=$DOCKER_CLOUD_USER export DOCKER_PASS=$DOCKER_CLOUD_PASSWORD - for IMAGE in flowapi flowmachine flowdb flowdb-oracle flowdb-synthetic-data flowdb-testdata flowauth flowkit-examples flowetl autoflow; do + for IMAGE in flowapi flowmachine flowdb flowdb-oracle flowdb-synthetic-data flowdb-testdata flowauth flowkit-examples flowetl; do docker-retag flowminder/$IMAGE:$CIRCLE_SHA1 ${<< parameters.tag >>:-latest} done @@ -1106,8 +1057,6 @@ workflows: requires: - build_flowdb_synthdata <<: *run_always_org_context - - run_autoflow_tests: - <<: *run_always_org_context - build_docker_image: name: build_examples component: flowkit-examples @@ -1139,11 +1088,6 @@ workflows: component: flowapi component_path: flowapi <<: *run_always_org_context - - build_docker_image: - name: build_autoflow - component: autoflow - component_path: autoflow - <<: *run_always_org_context - run_flowauth_end_to_end_tests: requires: - build_flowauth @@ -1236,8 +1180,6 @@ workflows: - build_flowetl_wheel - build_flowkit_jwt_generator_wheel - run_flowmachine_tests - - run_autoflow_tests - - build_autoflow - build_flowdb_oracle <<: *master_only_org_context - retag_images: diff --git a/Makefile b/Makefile index a4d8fcf410..6e5e0ab47c 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ DOCKER_COMPOSE_TESTDATA_FILE_BUILD ?= docker-compose-testdata-build.yml DOCKER_COMPOSE_SYNTHETICDATA_FILE_BUILD ?= docker-compose-syntheticdata-build.yml DOCKER_SERVICES ?= flowdb flowapi flowmachine flowauth flowmachine_query_locker flowetl flowetl_db worked_examples DOCKER_SERVICES_TO_START = $(patsubst flowdb%,flowdb,$(DOCKER_SERVICES)) -services := flowmachine flowmachine_query_locker flowapi flowauth flowdb worked_examples flowdb_testdata flowdb_synthetic_data flowetl flowetl_db autoflow +services := flowmachine flowmachine_query_locker flowapi flowauth flowdb worked_examples flowdb_testdata flowdb_synthetic_data flowetl flowetl_db space := space += DOCKER_COMPOSE := docker compose -f $(DOCKER_COMPOSE_FILE) diff --git a/autoflow.Dockerfile b/autoflow.Dockerfile deleted file mode 100644 index cb8756b7e5..0000000000 --- a/autoflow.Dockerfile +++ /dev/null @@ -1,47 +0,0 @@ -FROM python:3.8-slim - -ARG SOURCE_VERSION=0+unknown -ENV SOURCE_VERSION=${SOURCE_VERSION} -ENV SOURCE_TREE=FlowKit-${SOURCE_VERSION} -COPY . /${SOURCE_TREE}/ - -ENV AUTOFLOW_DB_URI="sqlite:////tmp/test.db" -ENV AUTOFLOW_INPUTS_DIR=/mounts/inputs -ENV AUTOFLOW_OUTPUTS_DIR=/mounts/outputs -ENV PREFECT__USER_CONFIG_PATH=/${SOURCE_TREE}/autoflow/config/config.toml -ENV PREFECT__ASCIIDOC_TEMPLATE_PATH=/${SOURCE_TREE}/autoflow/config/asciidoc_extended.tpl - -WORKDIR /${SOURCE_TREE}/autoflow -RUN apt-get update -yqq \ - && apt-get upgrade -yqq \ - && apt-get upgrade -yqq git \ - && apt-get install -yqq pandoc ruby gcc \ - && gem install bundler \ - && bundle install \ - && apt-get remove -y gcc \ - && apt-get autoremove -y \ - && apt-get clean \ - && rm -rf \ - /var/lib/apt/lists/* \ - /tmp/* \ - /var/tmp/* \ - /usr/share/man \ - /usr/share/doc \ - /usr/share/doc-base - -RUN apt-get update -yqq \ - && apt-get install -yqq gcc g++ \ - && pip install -U pip && pip install .[postgres,examples] \ - && apt-get remove -y gcc g++ \ - && apt-get autoremove -y \ - && apt-get clean \ - && rm -rf \ - /var/lib/apt/lists/* \ - /tmp/* \ - /var/tmp/* \ - /usr/share/man \ - /usr/share/doc \ - /usr/share/doc-base - - -CMD ["python", "-m", "autoflow"] diff --git a/autoflow.Dockerfile.dockerignore b/autoflow.Dockerfile.dockerignore deleted file mode 100644 index a6c01592b4..0000000000 --- a/autoflow.Dockerfile.dockerignore +++ /dev/null @@ -1,20 +0,0 @@ -# This .dockerignore file follows the "whitelisting" approach described here: -# https://youknowfordevs.com/2018/12/07/getting-control-of-your-dockerignore-files.html - -# -# First, exclude everything by default: -# -* - -# -# Now un-exclude only those folders and files that are needed for building the image: -# -!autoflow/ -!config/ -!MANIFEST.in -!setup.cfg -!setup.py -!versioneer.py -!README.md -!Gemfile -!Gemfile.lock diff --git a/autoflow/.coveragerc b/autoflow/.coveragerc deleted file mode 100644 index 20d31ad1c4..0000000000 --- a/autoflow/.coveragerc +++ /dev/null @@ -1,24 +0,0 @@ -[run] -branch = True -source = . -include = autoflow/* - -[report] -include = autoflow/* -omit = - */python?.?/* - */venv/* - */site-packages/nose/* - */tests/* - */_version.py - */versioneer.py - *__init__* - -exclude_lines = - # Need to explicitly re-enable the standard pragma - pragma: no cover - - if __name__ == '__main__': - def __repr__ - -show_missing = True diff --git a/autoflow/.gitattributes b/autoflow/.gitattributes deleted file mode 100644 index 8ab1ec31b9..0000000000 --- a/autoflow/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -autoflow/_version.py export-subst diff --git a/autoflow/Gemfile b/autoflow/Gemfile deleted file mode 100644 index f175275953..0000000000 --- a/autoflow/Gemfile +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -source "https://rubygems.org" - -gem "asciidoctor" -gem "asciidoctor-pdf", "~> 1.6.2" diff --git a/autoflow/Gemfile.lock b/autoflow/Gemfile.lock deleted file mode 100644 index 35ddfeb299..0000000000 --- a/autoflow/Gemfile.lock +++ /dev/null @@ -1,61 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - Ascii85 (1.1.0) - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) - afm (0.2.2) - asciidoctor (2.0.20) - asciidoctor-pdf (1.6.2) - asciidoctor (~> 2.0) - concurrent-ruby (~> 1.1) - prawn (~> 2.4.0) - prawn-icon (~> 3.0.0) - prawn-svg (~> 0.32.0) - prawn-table (~> 0.2.0) - prawn-templates (~> 0.1.0) - safe_yaml (~> 1.0.0) - treetop (~> 1.6.0) - concurrent-ruby (1.1.9) - css_parser (1.11.0) - addressable - hashery (2.1.2) - pdf-core (0.9.0) - pdf-reader (2.8.0) - Ascii85 (~> 1.0) - afm (~> 0.2.1) - hashery (~> 2.0) - ruby-rc4 - ttfunk - polyglot (0.3.5) - prawn (2.4.0) - pdf-core (~> 0.9.0) - ttfunk (~> 1.7) - prawn-icon (3.0.0) - prawn (>= 1.1.0, < 3.0.0) - prawn-svg (0.32.0) - css_parser (~> 1.6) - prawn (>= 0.11.1, < 3) - rexml (~> 3.2) - prawn-table (0.2.2) - prawn (>= 1.3.0, < 3.0.0) - prawn-templates (0.1.2) - pdf-reader (~> 2.0) - prawn (~> 2.2) - public_suffix (4.0.6) - rexml (3.2.5) - ruby-rc4 (0.1.5) - safe_yaml (1.0.5) - treetop (1.6.11) - polyglot (~> 0.3) - ttfunk (1.7.0) - -PLATFORMS - ruby - -DEPENDENCIES - asciidoctor - asciidoctor-pdf (~> 1.6.2) - -BUNDLED WITH - 2.2.33 diff --git a/autoflow/MANIFEST.in b/autoflow/MANIFEST.in deleted file mode 100644 index 6de876e64f..0000000000 --- a/autoflow/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include versioneer.py -include autoflow/_version.py diff --git a/autoflow/Pipfile b/autoflow/Pipfile deleted file mode 100644 index ea64b7ba13..0000000000 --- a/autoflow/Pipfile +++ /dev/null @@ -1,40 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -black = {extras = ["jupyter"],version = "==23.7.0"} # required by papermill -descartes = "*" -flowclient = {editable = true,path = "./../flowclient"} -geopandas = "*" -get-secret-or-env-var = "*" -ipykernel = "*" -marshmallow = ">=3.0.0" -matplotlib = "*" -networkx = "*" -nteract-scrapbook = "*" -pandas = "*" -papermill = ">=1.2.1" -pendulum = "*" -distributed = "*" -prefect = "<2.0" -psycopg2-binary = "*" -pyyaml = ">=6.0.1" -sh = "*" -sqlalchemy = "<2.0.0" -nbconvert = "*" -nbformat = "*" - -[dev-packages] -ipywidgets = "*" -jupyterlab = "*" -distributed = "*" # Pinned because prefect is pinned -prefect = {version="*", extras = ["viz"]} -pytest = "*" -pytest-cov = "*" -sqlalchemy-utils = "*" -testing-postgresql = "*" - -[requires] -python_version = "3.8.5" diff --git a/autoflow/Pipfile.lock b/autoflow/Pipfile.lock deleted file mode 100644 index 9cae9af443..0000000000 --- a/autoflow/Pipfile.lock +++ /dev/null @@ -1,4119 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "4a4af0df57ad0ea7f05640e8b79c8b4633f88d67b572528644fe01f91e3db56f" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.8.5" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "ansiwrap": { - "hashes": [ - "sha256:7b053567c88e1ad9eed030d3ac41b722125e4c1271c8a99ade797faff1f49fb1", - "sha256:ca0c740734cde59bf919f8ff2c386f74f9a369818cdc60efe94893d01ea8d9b7" - ], - "version": "==0.8.4" - }, - "anyio": { - "hashes": [ - "sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f", - "sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a" - ], - "markers": "python_version >= '3.8'", - "version": "==4.0.0" - }, - "appnope": { - "hashes": [ - "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24", - "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e" - ], - "markers": "platform_system == 'Darwin'", - "version": "==0.1.3" - }, - "asttokens": { - "hashes": [ - "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", - "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0" - ], - "version": "==2.4.1" - }, - "attrs": { - "hashes": [ - "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1.0" - }, - "backcall": { - "hashes": [ - "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e", - "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255" - ], - "version": "==0.2.0" - }, - "beautifulsoup4": { - "hashes": [ - "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da", - "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a" - ], - "markers": "python_full_version >= '3.6.0'", - "version": "==4.12.2" - }, - "black": { - "extras": [ - "jupyter" - ], - "hashes": [ - "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3", - "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb", - "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087", - "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320", - "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6", - "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3", - "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc", - "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f", - "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587", - "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91", - "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a", - "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad", - "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926", - "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9", - "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be", - "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd", - "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96", - "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491", - "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2", - "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a", - "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f", - "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995" - ], - "markers": "python_version >= '3.8'", - "version": "==23.7.0" - }, - "bleach": { - "hashes": [ - "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe", - "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6" - ], - "markers": "python_version >= '3.8'", - "version": "==6.1.0" - }, - "certifi": { - "hashes": [ - "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", - "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" - ], - "markers": "python_version >= '3.6'", - "version": "==2023.7.22" - }, - "charset-normalizer": { - "hashes": [ - "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5", - "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93", - "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a", - "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d", - "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c", - "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1", - "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58", - "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2", - "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557", - "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147", - "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041", - "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2", - "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2", - "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7", - "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296", - "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690", - "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67", - "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57", - "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597", - "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846", - "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b", - "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97", - "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c", - "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62", - "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa", - "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f", - "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e", - "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821", - "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3", - "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4", - "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb", - "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727", - "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514", - "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d", - "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761", - "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55", - "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f", - "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c", - "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034", - "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6", - "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae", - "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1", - "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14", - "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1", - "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228", - "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708", - "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48", - "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f", - "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5", - "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f", - "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4", - "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8", - "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff", - "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61", - "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b", - "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97", - "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b", - "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605", - "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728", - "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d", - "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c", - "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf", - "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673", - "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1", - "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b", - "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41", - "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8", - "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f", - "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4", - "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008", - "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9", - "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5", - "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f", - "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e", - "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273", - "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45", - "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e", - "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656", - "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e", - "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c", - "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2", - "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72", - "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056", - "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397", - "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42", - "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd", - "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3", - "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213", - "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf", - "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.1" - }, - "click": { - "hashes": [ - "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", - "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.7" - }, - "click-plugins": { - "hashes": [ - "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b", - "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8" - ], - "version": "==1.1.1" - }, - "cligj": { - "hashes": [ - "sha256:a4bc13d623356b373c2c27c53dbd9c68cae5d526270bfa71f6c6fa69669c6b27", - "sha256:c1ca117dbce1fe20a5809dc96f01e1c2840f6dcc939b3ddbb1111bf330ba82df" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' and python_version < '4'", - "version": "==0.7.2" - }, - "cloudpickle": { - "hashes": [ - "sha256:246ee7d0c295602a036e86369c77fecda4ab17b506496730f2f576d9016fd9c7", - "sha256:996d9a482c6fb4f33c1a35335cf8afd065d2a56e973270364840712d9131a882" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, - "comm": { - "hashes": [ - "sha256:354e40a59c9dd6db50c5cc6b4acc887d82e9603787f83b68c01a80a923984d15", - "sha256:6d52794cba11b36ed9860999cd10fd02d6b2eac177068fdd585e1e2f8a96e67a" - ], - "markers": "python_version >= '3.6'", - "version": "==0.1.4" - }, - "contourpy": { - "hashes": [ - "sha256:059c3d2a94b930f4dafe8105bcdc1b21de99b30b51b5bce74c753686de858cb6", - "sha256:0683e1ae20dc038075d92e0e0148f09ffcefab120e57f6b4c9c0f477ec171f33", - "sha256:07d6f11dfaf80a84c97f1a5ba50d129d9303c5b4206f776e94037332e298dda8", - "sha256:081f3c0880712e40effc5f4c3b08feca6d064cb8cfbb372ca548105b86fd6c3d", - "sha256:0e48694d6a9c5a26ee85b10130c77a011a4fedf50a7279fa0bdaf44bafb4299d", - "sha256:11b836b7dbfb74e049c302bbf74b4b8f6cb9d0b6ca1bf86cfa8ba144aedadd9c", - "sha256:19557fa407e70f20bfaba7d55b4d97b14f9480856c4fb65812e8a05fe1c6f9bf", - "sha256:229a25f68046c5cf8067d6d6351c8b99e40da11b04d8416bf8d2b1d75922521e", - "sha256:24216552104ae8f3b34120ef84825400b16eb6133af2e27a190fdc13529f023e", - "sha256:3b53d5769aa1f2d4ea407c65f2d1d08002952fac1d9e9d307aa2e1023554a163", - "sha256:3de23ca4f381c3770dee6d10ead6fff524d540c0f662e763ad1530bde5112532", - "sha256:407d864db716a067cc696d61fa1ef6637fedf03606e8417fe2aeed20a061e6b2", - "sha256:41339b24471c58dc1499e56783fedc1afa4bb018bcd035cfb0ee2ad2a7501ef8", - "sha256:462c59914dc6d81e0b11f37e560b8a7c2dbab6aca4f38be31519d442d6cde1a1", - "sha256:46e24f5412c948d81736509377e255f6040e94216bf1a9b5ea1eaa9d29f6ec1b", - "sha256:498e53573e8b94b1caeb9e62d7c2d053c263ebb6aa259c81050766beb50ff8d9", - "sha256:4ebf42695f75ee1a952f98ce9775c873e4971732a87334b099dde90b6af6a916", - "sha256:4f9147051cb8fdb29a51dc2482d792b3b23e50f8f57e3720ca2e3d438b7adf23", - "sha256:549174b0713d49871c6dee90a4b499d3f12f5e5f69641cd23c50a4542e2ca1eb", - "sha256:560f1d68a33e89c62da5da4077ba98137a5e4d3a271b29f2f195d0fba2adcb6a", - "sha256:566f0e41df06dfef2431defcfaa155f0acfa1ca4acbf8fd80895b1e7e2ada40e", - "sha256:56de98a2fb23025882a18b60c7f0ea2d2d70bbbcfcf878f9067234b1c4818442", - "sha256:66544f853bfa85c0d07a68f6c648b2ec81dafd30f272565c37ab47a33b220684", - "sha256:6c06e4c6e234fcc65435223c7b2a90f286b7f1b2733058bdf1345d218cc59e34", - "sha256:6d0a8efc258659edc5299f9ef32d8d81de8b53b45d67bf4bfa3067f31366764d", - "sha256:70e5a10f8093d228bb2b552beeb318b8928b8a94763ef03b858ef3612b29395d", - "sha256:8394e652925a18ef0091115e3cc191fef350ab6dc3cc417f06da66bf98071ae9", - "sha256:8636cd2fc5da0fb102a2504fa2c4bea3cbc149533b345d72cdf0e7a924decc45", - "sha256:93df44ab351119d14cd1e6b52a5063d3336f0754b72736cc63db59307dabb718", - "sha256:96ba37c2e24b7212a77da85004c38e7c4d155d3e72a45eeaf22c1f03f607e8ab", - "sha256:a10dab5ea1bd4401c9483450b5b0ba5416be799bbd50fc7a6cc5e2a15e03e8a3", - "sha256:a66045af6cf00e19d02191ab578a50cb93b2028c3eefed999793698e9ea768ae", - "sha256:a75cc163a5f4531a256f2c523bd80db509a49fc23721b36dd1ef2f60ff41c3cb", - "sha256:b04c2f0adaf255bf756cf08ebef1be132d3c7a06fe6f9877d55640c5e60c72c5", - "sha256:ba42e3810999a0ddd0439e6e5dbf6d034055cdc72b7c5c839f37a7c274cb4eba", - "sha256:bfc8a5e9238232a45ebc5cb3bfee71f1167064c8d382cadd6076f0d51cff1da0", - "sha256:c5bd5680f844c3ff0008523a71949a3ff5e4953eb7701b28760805bc9bcff217", - "sha256:c84fdf3da00c2827d634de4fcf17e3e067490c4aea82833625c4c8e6cdea0887", - "sha256:ca6fab080484e419528e98624fb5c4282148b847e3602dc8dbe0cb0669469887", - "sha256:d0c188ae66b772d9d61d43c6030500344c13e3f73a00d1dc241da896f379bb62", - "sha256:d6ab42f223e58b7dac1bb0af32194a7b9311065583cc75ff59dcf301afd8a431", - "sha256:dfe80c017973e6a4c367e037cb31601044dd55e6bfacd57370674867d15a899b", - "sha256:e0c02b75acfea5cab07585d25069207e478d12309557f90a61b5a3b4f77f46ce", - "sha256:e30aaf2b8a2bac57eb7e1650df1b3a4130e8d0c66fc2f861039d507a11760e1b", - "sha256:eafbef886566dc1047d7b3d4b14db0d5b7deb99638d8e1be4e23a7c7ac59ff0f", - "sha256:efe0fab26d598e1ec07d72cf03eaeeba8e42b4ecf6b9ccb5a356fde60ff08b85", - "sha256:f08e469821a5e4751c97fcd34bcb586bc243c39c2e39321822060ba902eac49e", - "sha256:f1eaac5257a8f8a047248d60e8f9315c6cff58f7803971170d952555ef6344a7", - "sha256:f29fb0b3f1217dfe9362ec55440d0743fe868497359f2cf93293f4b2701b8251", - "sha256:f44d78b61740e4e8c71db1cf1fd56d9050a4747681c59ec1094750a658ceb970", - "sha256:f6aec19457617ef468ff091669cca01fa7ea557b12b59a7908b9474bb9674cf0", - "sha256:f9dc7f933975367251c1b34da882c4f0e0b2e24bb35dc906d2f598a40b72bfc7" - ], - "markers": "python_version >= '3.8'", - "version": "==1.1.1" - }, - "croniter": { - "hashes": [ - "sha256:4cb064ce2d8f695b3b078be36ff50115cf8ac306c10a7e8653ee2a5b534673d7", - "sha256:d199b2ec3ea5e82988d1f72022433c5f9302b3b3ea9e6bfd6a1518f6ea5e700a" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.0.1" - }, - "cycler": { - "hashes": [ - "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", - "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c" - ], - "markers": "python_version >= '3.8'", - "version": "==0.12.1" - }, - "dask": { - "hashes": [ - "sha256:32b34986519b7ddc0947c8ca63c2fc81b964e4c208dfb5cbf9f4f8aec92d152b", - "sha256:4f4c28ac406e81b8f21b5be4b31b21308808f3e0e7c7e2f4a914f16476d9941b" - ], - "markers": "python_version >= '3.8'", - "version": "==2023.5.0" - }, - "debugpy": { - "hashes": [ - "sha256:125b9a637e013f9faac0a3d6a82bd17c8b5d2c875fb6b7e2772c5aba6d082332", - "sha256:12af2c55b419521e33d5fb21bd022df0b5eb267c3e178f1d374a63a2a6bdccd0", - "sha256:3c6fb41c98ec51dd010d7ed650accfd07a87fe5e93eca9d5f584d0578f28f35f", - "sha256:46ab6780159eeabb43c1495d9c84cf85d62975e48b6ec21ee10c95767c0590aa", - "sha256:57161629133113c97b387382045649a2b985a348f0c9366e22217c87b68b73c6", - "sha256:5d9de202f5d42e62f932507ee8b21e30d49aae7e46d5b1dd5c908db1d7068637", - "sha256:60009b132c91951354f54363f8ebdf7457aeb150e84abba5ae251b8e9f29a8a6", - "sha256:61eab4a4c8b6125d41a34bad4e5fe3d2cc145caecd63c3fe953be4cc53e65bf8", - "sha256:7fb95ca78f7ac43393cd0e0f2b6deda438ec7c5e47fa5d38553340897d2fbdfb", - "sha256:8cd0197141eb9e8a4566794550cfdcdb8b3db0818bdf8c49a8e8f8053e56e38b", - "sha256:9c9b0ac1ce2a42888199df1a1906e45e6f3c9555497643a85e0bf2406e3ffbc4", - "sha256:a64093656c4c64dc6a438e11d59369875d200bd5abb8f9b26c1f5f723622e153", - "sha256:a8b7a2fd27cd9f3553ac112f356ad4ca93338feadd8910277aff71ab24d8775f", - "sha256:b05a6b503ed520ad58c8dc682749113d2fd9f41ffd45daec16e558ca884008cd", - "sha256:bdc5ef99d14b9c0fcb35351b4fbfc06ac0ee576aeab6b2511702e5a648a2e595", - "sha256:e3412f9faa9ade82aa64a50b602544efcba848c91384e9f93497a458767e6926", - "sha256:ef54404365fae8d45cf450d0544ee40cefbcb9cb85ea7afe89a963c27028261e", - "sha256:ef9ab7df0b9a42ed9c878afd3eaaff471fce3fa73df96022e1f5c9f8f8c87ada" - ], - "markers": "python_version >= '3.8'", - "version": "==1.8.0" - }, - "decorator": { - "hashes": [ - "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", - "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186" - ], - "markers": "python_version >= '3.5'", - "version": "==5.1.1" - }, - "defusedxml": { - "hashes": [ - "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", - "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.7.1" - }, - "descartes": { - "hashes": [ - "sha256:135a502146af5ed6ff359975e2ebc5fa4b71b5432c355c2cafdc6dea1337035b", - "sha256:4c62dc41109689d03e4b35de0a2bcbdeeb81047badc607c4415d5c753bd683af", - "sha256:b7e412e7e6e294412f1d0f661f187babc970088c2456089e6801eebb043c2e1b" - ], - "index": "pypi", - "version": "==1.1.0" - }, - "distributed": { - "hashes": [ - "sha256:73ce33bd2460bd45ffc793ffdf9066bd2a3b6bbc65079f74f5147eafcda9b1cb", - "sha256:74e3f7f68d4dc435a3591ae1ad8ce7d5a11211fd22692e39c7e50aa11bf7e385" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==2023.5.0" - }, - "docker": { - "hashes": [ - "sha256:aa6d17830045ba5ef0168d5eaa34d37beeb113948c413affe1d5991fc11f9a20", - "sha256:aecd2277b8bf8e506e484f6ab7aec39abe0038e29fa4a6d3ba86c3fe01844ed9" - ], - "markers": "python_version >= '3.7'", - "version": "==6.1.3" - }, - "entrypoints": { - "hashes": [ - "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4", - "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f" - ], - "markers": "python_version >= '3.6'", - "version": "==0.4" - }, - "exceptiongroup": { - "hashes": [ - "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9", - "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3" - ], - "markers": "python_version < '3.11'", - "version": "==1.1.3" - }, - "executing": { - "hashes": [ - "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147", - "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc" - ], - "markers": "python_version >= '3.5'", - "version": "==2.0.1" - }, - "fastjsonschema": { - "hashes": [ - "sha256:06dc8680d937628e993fa0cd278f196d20449a1adc087640710846b324d422ea", - "sha256:aec6a19e9f66e9810ab371cc913ad5f4e9e479b63a7072a2cd060a9369e329a8" - ], - "version": "==2.18.1" - }, - "fiona": { - "hashes": [ - "sha256:35dae4b0308eb44617cdc4461ceb91f891d944fdebbcba5479efe524ec5db8de", - "sha256:374efe749143ecb5cfdd79b585d83917d2bf8ecfbfc6953c819586b336ce9c63", - "sha256:3ea04ec2d8c57b5f81a31200fb352cb3242aa106fc3e328963f30ffbdf0ff7c8", - "sha256:4877cc745d9e82b12b3eafce3719db75759c27bd8a695521202135b36b58c2e7", - "sha256:4df21906235928faad856c288cfea0298e9647f09c9a69a230535cbc8eadfa21", - "sha256:5b4c6a3df53bee8f85bb46685562b21b43346be1fe96419f18f70fa1ab8c561c", - "sha256:5f40a40529ecfca5294260316cf987a0420c77a2f0cf0849f529d1afbccd093e", - "sha256:6ad04c1877b9fd742871b11965606c6a52f40706f56a48d66a87cc3073943828", - "sha256:6d9f29e9bcbb33232ff7fa98b4a3c2234db910c1dc6c4147fc36c0b8b930f2e0", - "sha256:7ede8ddc798f3d447536080c6db9a5fb73733ad8bdb190cb65eed4e289dd4c50", - "sha256:81d502369493687746cb8d3cd77e5ada4447fb71d513721c9a1826e4fb32b23a", - "sha256:8b098054a27c12afac4f819f98cb4d4bf2db9853f70b0c588d7d97d26e128c39", - "sha256:8b53ce8de773fcd5e2e102e833c8c58479edd8796a522f3d83ef9e08b62bfeea", - "sha256:99e2604332caa7692855c2ae6ed91e1fffdf9b59449aa8032dd18e070e59a2f7", - "sha256:9a2da52f865db1aff0eaf41cdd4c87a7c079b3996514e8e7a1ca38457309e825", - "sha256:9fb9a24a8046c724787719e20557141b33049466145fc3e665764ac7caf5748c", - "sha256:ac2c250f509ec19fad7959d75b531984776517ef3c1222d1cc5b4f962825880b", - "sha256:bd2355e859a1cd24a3e485c6dc5003129f27a2051629def70036535ffa7e16a4", - "sha256:c521e1135c78dec0d7774303e5a1b4c62e0efb0e602bb8f167550ef95e0a2691", - "sha256:ce3b29230ef70947ead4e701f3f82be81082b7f37fd4899009b1445cc8fc276a", - "sha256:cfef6db5b779d463298b1113b50daa6c5b55f26f834dc9e37752116fa17277c1", - "sha256:d722d7f01a66f4ab6cd08d156df3fdb92f0669cf5f8708ddcb209352f416f241", - "sha256:f1af08da4ecea5036cb81c9131946be4404245d1b434b5b24fd3871a1d4030d9", - "sha256:fce4b1dd98810cabccdaa1828430c7402d283295c2ae31bea4f34188ea9e88d7" - ], - "markers": "python_version >= '3.7'", - "version": "==1.9.5" - }, - "flowclient": { - "editable": true, - "path": "./../flowclient" - }, - "fonttools": { - "hashes": [ - "sha256:10003ebd81fec0192c889e63a9c8c63f88c7d72ae0460b7ba0cd2a1db246e5ad", - "sha256:10b3922875ffcba636674f406f9ab9a559564fdbaa253d66222019d569db869c", - "sha256:13a9a185259ed144def3682f74fdcf6596f2294e56fe62dfd2be736674500dba", - "sha256:17dbc2eeafb38d5d0e865dcce16e313c58265a6d2d20081c435f84dc5a9d8212", - "sha256:18a2477c62a728f4d6e88c45ee9ee0229405e7267d7d79ce1f5ce0f3e9f8ab86", - "sha256:18eefac1b247049a3a44bcd6e8c8fd8b97f3cad6f728173b5d81dced12d6c477", - "sha256:1952c89a45caceedf2ab2506d9a95756e12b235c7182a7a0fff4f5e52227204f", - "sha256:1cf9e974f63b1080b1d2686180fc1fbfd3bfcfa3e1128695b5de337eb9075cef", - "sha256:1e09da7e8519e336239fbd375156488a4c4945f11c4c5792ee086dd84f784d02", - "sha256:2062542a7565091cea4cc14dd99feff473268b5b8afdee564f7067dd9fff5860", - "sha256:25d3da8a01442cbc1106490eddb6d31d7dffb38c1edbfabbcc8db371b3386d72", - "sha256:34f713dad41aa21c637b4e04fe507c36b986a40f7179dcc86402237e2d39dcd3", - "sha256:360201d46165fc0753229afe785900bc9596ee6974833124f4e5e9f98d0f592b", - "sha256:3b7ad05b2beeebafb86aa01982e9768d61c2232f16470f9d0d8e385798e37184", - "sha256:4c54466f642d2116686268c3e5f35ebb10e49b0d48d41a847f0e171c785f7ac7", - "sha256:4d9740e3783c748521e77d3c397dc0662062c88fd93600a3c2087d3d627cd5e5", - "sha256:4f88cae635bfe4bbbdc29d479a297bb525a94889184bb69fa9560c2d4834ddb9", - "sha256:51669b60ee2a4ad6c7fc17539a43ffffc8ef69fd5dbed186a38a79c0ac1f5db7", - "sha256:5db46659cfe4e321158de74c6f71617e65dc92e54980086823a207f1c1c0e24b", - "sha256:5f37e31291bf99a63328668bb83b0669f2688f329c4c0d80643acee6e63cd933", - "sha256:6bb5ea9076e0e39defa2c325fc086593ae582088e91c0746bee7a5a197be3da0", - "sha256:748015d6f28f704e7d95cd3c808b483c5fb87fd3eefe172a9da54746ad56bfb6", - "sha256:7bbbf8174501285049e64d174e29f9578495e1b3b16c07c31910d55ad57683d8", - "sha256:884ef38a5a2fd47b0c1291647b15f4e88b9de5338ffa24ee52c77d52b4dfd09c", - "sha256:8da417431bfc9885a505e86ba706f03f598c85f5a9c54f67d63e84b9948ce590", - "sha256:95e974d70238fc2be5f444fa91f6347191d0e914d5d8ae002c9aa189572cc215", - "sha256:9648518ef687ba818db3fcc5d9aae27a369253ac09a81ed25c3867e8657a0680", - "sha256:9a2f0aa6ca7c9bc1058a9d0b35483d4216e0c1bbe3962bc62ce112749954c7b8", - "sha256:9c36da88422e0270fbc7fd959dc9749d31a958506c1d000e16703c2fce43e3d0", - "sha256:9c60ecfa62839f7184f741d0509b5c039d391c3aff71dc5bc57b87cc305cff3b", - "sha256:9f727c3e3d08fd25352ed76cc3cb61486f8ed3f46109edf39e5a60fc9fecf6ca", - "sha256:a7a06f8d95b7496e53af80d974d63516ffb263a468e614978f3899a6df52d4b3", - "sha256:ad0b3f6342cfa14be996971ea2b28b125ad681c6277c4cd0fbdb50340220dfb6", - "sha256:b2adca1b46d69dce4a37eecc096fe01a65d81a2f5c13b25ad54d5430ae430b13", - "sha256:b84a1c00f832feb9d0585ca8432fba104c819e42ff685fcce83537e2e7e91204", - "sha256:bb6d2f8ef81ea076877d76acfb6f9534a9c5f31dc94ba70ad001267ac3a8e56f", - "sha256:bf11e2cca121df35e295bd34b309046c29476ee739753bc6bc9d5050de319273", - "sha256:d21099b411e2006d3c3e1f9aaf339e12037dbf7bf9337faf0e93ec915991f43b", - "sha256:d4071bd1c183b8d0b368cc9ed3c07a0f6eb1bdfc4941c4c024c49a35429ac7cd", - "sha256:e117a92b07407a061cde48158c03587ab97e74e7d73cb65e6aadb17af191162a", - "sha256:f7a58eb5e736d7cf198eee94844b81c9573102ae5989ebcaa1d1a37acd04b33d", - "sha256:fe9b1ec799b6086460a7480e0f55c447b1aca0a4eecc53e444f639e967348896" - ], - "markers": "python_version >= '3.8'", - "version": "==4.43.1" - }, - "fsspec": { - "hashes": [ - "sha256:330c66757591df346ad3091a53bd907e15348c2ba17d63fd54f5c39c4457d2a5", - "sha256:346a8f024efeb749d2a5fca7ba8854474b1ff9af7c3faaf636a4548781136529" - ], - "markers": "python_version >= '3.8'", - "version": "==2023.10.0" - }, - "geopandas": { - "hashes": [ - "sha256:101cfd0de54bcf9e287a55b5ea17ebe0db53a5e25a28bacf100143d0507cabd9", - "sha256:e5b56d9c20800c77bcc0c914db3f27447a37b23b2cd892be543f5001a694a968" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==0.13.2" - }, - "get-secret-or-env-var": { - "hashes": [ - "sha256:669e85819ac680e980df7161b4a3b98ddd7253c703e8dbf2b16f36dea3214c60" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==0.0.2" - }, - "greenlet": { - "hashes": [ - "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174", - "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd", - "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa", - "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a", - "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec", - "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565", - "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d", - "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c", - "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234", - "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d", - "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546", - "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2", - "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74", - "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de", - "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd", - "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9", - "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3", - "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846", - "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2", - "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353", - "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8", - "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166", - "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206", - "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b", - "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d", - "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe", - "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997", - "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445", - "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0", - "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96", - "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884", - "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6", - "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1", - "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619", - "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94", - "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4", - "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1", - "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63", - "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd", - "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a", - "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376", - "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57", - "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16", - "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e", - "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc", - "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a", - "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c", - "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5", - "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a", - "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72", - "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9", - "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9", - "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e", - "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8", - "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65", - "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064", - "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36" - ], - "markers": "python_version >= '3.7'", - "version": "==3.0.1" - }, - "h11": { - "hashes": [ - "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", - "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761" - ], - "markers": "python_version >= '3.7'", - "version": "==0.14.0" - }, - "httpcore": { - "hashes": [ - "sha256:13b5e5cd1dca1a6636a6aaea212b19f4f85cd88c366a2b82304181b769aab3c9", - "sha256:adc5398ee0a476567bf87467063ee63584a8bce86078bf748e48754f60202ced" - ], - "markers": "python_version >= '3.8'", - "version": "==0.18.0" - }, - "httpx": { - "hashes": [ - "sha256:181ea7f8ba3a82578be86ef4171554dd45fec26a02556a744db029a0a27b7100", - "sha256:47ecda285389cb32bb2691cc6e069e3ab0205956f681c5b2ad2325719751d875" - ], - "markers": "python_version >= '3.8'", - "version": "==0.25.0" - }, - "idna": { - "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" - ], - "markers": "python_version >= '3.5'", - "version": "==3.4" - }, - "importlib-metadata": { - "hashes": [ - "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb", - "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743" - ], - "markers": "python_version < '3.10'", - "version": "==6.8.0" - }, - "importlib-resources": { - "hashes": [ - "sha256:9d48dcccc213325e810fd723e7fbb45ccb39f6cf5c31f00cf2b965f5f10f3cb9", - "sha256:aa50258bbfa56d4e33fbd8aa3ef48ded10d1735f11532b8df95388cc6bdb7e83" - ], - "markers": "python_version < '3.10'", - "version": "==6.1.0" - }, - "ipykernel": { - "hashes": [ - "sha256:3ba3dc97424b87b31bb46586b5167b3161b32d7820b9201a9e698c71e271602c", - "sha256:553856658eb8430bbe9653ea041a41bff63e9606fc4628873fc92a6cf3abd404" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==6.26.0" - }, - "ipython": { - "hashes": [ - "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363", - "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c" - ], - "markers": "python_version >= '3.3'", - "version": "==8.12.3" - }, - "ipywidgets": { - "hashes": [ - "sha256:2b88d728656aea3bbfd05d32c747cfd0078f9d7e159cf982433b58ad717eed7f", - "sha256:40211efb556adec6fa450ccc2a77d59ca44a060f4f9f136833df59c9f538e6e8" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.1" - }, - "jedi": { - "hashes": [ - "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd", - "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0" - ], - "markers": "python_version >= '3.6'", - "version": "==0.19.1" - }, - "jinja2": { - "hashes": [ - "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", - "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.2" - }, - "jsonschema": { - "hashes": [ - "sha256:c9ff4d7447eed9592c23a12ccee508baf0dd0d59650615e847feb6cdca74f392", - "sha256:eee9e502c788e89cb166d4d37f43084e3b64ab405c795c03d343a4dbc2c810fc" - ], - "markers": "python_version >= '3.8'", - "version": "==4.19.2" - }, - "jsonschema-specifications": { - "hashes": [ - "sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1", - "sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb" - ], - "markers": "python_version >= '3.8'", - "version": "==2023.7.1" - }, - "jupyter-client": { - "hashes": [ - "sha256:c3877aac7257ec68d79b5c622ce986bd2a992ca42f6ddc9b4dd1da50e89f7028", - "sha256:e8754066510ce456358df363f97eae64b50860f30dc1fe8c6771440db3be9a63" - ], - "markers": "python_version >= '3.8'", - "version": "==8.5.0" - }, - "jupyter-core": { - "hashes": [ - "sha256:880b86053bf298a8724994f95e99b99130659022a4f7f45f563084b6223861d3", - "sha256:e11e02cd8ae0a9de5c6c44abf5727df9f2581055afe00b22183f621ba3585805" - ], - "markers": "python_version >= '3.8'", - "version": "==5.5.0" - }, - "jupyterlab-pygments": { - "hashes": [ - "sha256:2405800db07c9f770863bcf8049a529c3dd4d3e28536638bd7c1c01d2748309f", - "sha256:7405d7fde60819d905a9fa8ce89e4cd830e318cdad22a0030f7a901da705585d" - ], - "markers": "python_version >= '3.7'", - "version": "==0.2.2" - }, - "jupyterlab-widgets": { - "hashes": [ - "sha256:3cf5bdf5b897bf3bccf1c11873aa4afd776d7430200f765e0686bd352487b58d", - "sha256:6005a4e974c7beee84060fdfba341a3218495046de8ae3ec64888e5fe19fdb4c" - ], - "markers": "python_version >= '3.7'", - "version": "==3.0.9" - }, - "kiwisolver": { - "hashes": [ - "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf", - "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e", - "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af", - "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f", - "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046", - "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3", - "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5", - "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71", - "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee", - "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3", - "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9", - "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b", - "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985", - "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea", - "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16", - "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89", - "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c", - "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9", - "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712", - "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342", - "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a", - "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958", - "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d", - "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a", - "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130", - "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff", - "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898", - "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b", - "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f", - "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265", - "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93", - "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929", - "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635", - "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709", - "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b", - "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb", - "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a", - "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920", - "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e", - "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544", - "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45", - "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390", - "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77", - "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355", - "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff", - "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4", - "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7", - "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20", - "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c", - "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162", - "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228", - "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437", - "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc", - "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a", - "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901", - "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4", - "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770", - "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525", - "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad", - "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a", - "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29", - "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90", - "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250", - "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d", - "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3", - "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54", - "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f", - "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1", - "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da", - "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238", - "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa", - "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523", - "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0", - "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205", - "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3", - "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4", - "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac", - "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9", - "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb", - "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced", - "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd", - "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0", - "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da", - "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18", - "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9", - "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276", - "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333", - "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b", - "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db", - "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126", - "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9", - "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09", - "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0", - "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec", - "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7", - "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff", - "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9", - "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192", - "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8", - "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d", - "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6", - "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797", - "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892", - "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f" - ], - "markers": "python_version >= '3.7'", - "version": "==1.4.5" - }, - "locket": { - "hashes": [ - "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632", - "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.0.0" - }, - "markupsafe": { - "hashes": [ - "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", - "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", - "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", - "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", - "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", - "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", - "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", - "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", - "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", - "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", - "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", - "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", - "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", - "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", - "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", - "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", - "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", - "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", - "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", - "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", - "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", - "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", - "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", - "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", - "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", - "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", - "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", - "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", - "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", - "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", - "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", - "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", - "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", - "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", - "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", - "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", - "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", - "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", - "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", - "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", - "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", - "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", - "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", - "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", - "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", - "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", - "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", - "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", - "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", - "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", - "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", - "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", - "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", - "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", - "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", - "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", - "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", - "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", - "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", - "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.3" - }, - "marshmallow": { - "hashes": [ - "sha256:5d2371bbe42000f2b3fb5eaa065224df7d8f8597bc19a1bbfa5bfe7fba8da889", - "sha256:684939db93e80ad3561392f47be0230743131560a41c5110684c16e21ade0a5c" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==3.20.1" - }, - "marshmallow-oneofschema": { - "hashes": [ - "sha256:62cd2099b29188c92493c2940ee79d1bf2f2619a71721664e5a98ec2faa58237", - "sha256:bd29410a9f2f7457a2b428286e2a80ef76b8ddc3701527dc1f935a88914b02f2" - ], - "markers": "python_version >= '3.6'", - "version": "==3.0.1" - }, - "matplotlib": { - "hashes": [ - "sha256:085c33b27561d9c04386789d5aa5eb4a932ddef43cfcdd0e01735f9a6e85ce0c", - "sha256:0a97af9d22e8ebedc9f00b043d9bbd29a375e9e10b656982012dded44c10fd77", - "sha256:122dcbf9be0086e2a95d9e5e0632dbf3bd5b65eaa68c369363310a6c87753059", - "sha256:12b4f6795efea037ce2d41e7c417ad8bd02d5719c6ad4a8450a0708f4a1cfb89", - "sha256:12f4c0dd8aa280d796c8772ea8265a14f11a04319baa3a16daa5556065e8baea", - "sha256:165c8082bf8fc0360c24aa4724a22eaadbfd8c28bf1ccf7e94d685cad48261e4", - "sha256:1990955b11e7918d256cf3b956b10997f405b7917a3f1c7d8e69c1d15c7b1930", - "sha256:1a6094c6f8e8d18db631754df4fe9a34dec3caf074f6869a7db09f18f9b1d6b2", - "sha256:1f9c6c16597af660433ab330b59ee2934b832ee1fabcaf5cbde7b2add840f31e", - "sha256:22d65d18b4ee8070a5fea5761d59293f1f9e2fac37ec9ce090463b0e629432fd", - "sha256:236024f582e40dac39bca592258888b38ae47a9fed7b8de652d68d3d02d47d2b", - "sha256:259999c05285cb993d7f2a419cea547863fa215379eda81f7254c9e932963729", - "sha256:272dba2f1b107790ed78ebf5385b8d14b27ad9e90419de340364b49fe549a993", - "sha256:336e88900c11441e458da01c8414fc57e04e17f9d3bb94958a76faa2652bcf6b", - "sha256:39018a2b17592448fbfdf4b8352955e6c3905359939791d4ff429296494d1a0c", - "sha256:3bf3a178c6504694cee8b88b353df0051583f2f6f8faa146f67115c27c856881", - "sha256:3f4e7fd5a6157e1d018ce2166ec8e531a481dd4a36f035b5c23edfe05a25419a", - "sha256:40e3b9b450c6534f07278310c4e34caff41c2a42377e4b9d47b0f8d3ac1083a2", - "sha256:498a08267dc69dd8f24c4b5d7423fa584d7ce0027ba71f7881df05fc09b89bb7", - "sha256:4aab27d9e33293389e3c1d7c881d414a72bdfda0fedc3a6bf46c6fa88d9b8015", - "sha256:55de4cf7cd0071b8ebf203981b53ab64f988a0a1f897a2dff300a1124e8bcd8b", - "sha256:591c123bed1cb4b9996fb60b41a6d89c2ec4943244540776c5f1283fb6960a53", - "sha256:67a410a9c9e07cbc83581eeea144bbe298870bf0ac0ee2f2e10a015ab7efee19", - "sha256:6eaa1cf0e94c936a26b78f6d756c5fbc12e0a58c8a68b7248a2a31456ce4e234", - "sha256:7153453669c9672b52095119fd21dd032d19225d48413a2871519b17db4b0fde", - "sha256:747c6191d2e88ae854809e69aa358dbf852ff1a5738401b85c1cc9012309897a", - "sha256:74bf57f505efea376097e948b7cdd87191a7ce8180616390aef496639edf601f", - "sha256:755bafc10a46918ce9a39980009b54b02dd249594e5adf52f9c56acfddb5d0b7", - "sha256:78b2136cc6c5415b78977e0e8c608647d597204b05b1d9089ccf513c7d913733", - "sha256:7baf98c5ad59c5c4743ea884bb025cbffa52dacdfdac0da3e6021a285a90377e", - "sha256:91e36a85ea639a1ba9f91427041eac064b04829945fe331a92617b6cb21d27e5", - "sha256:9c40cde976c36693cc0767e27cf5f443f91c23520060bd9496678364adfafe9c", - "sha256:a7240259b4b9cbc62381f6378cff4d57af539162a18e832c1e48042fabc40b6b", - "sha256:ac03377fd908aaee2312d0b11735753e907adb6f4d1d102de5e2425249693f6c", - "sha256:c568e80e1c17f68a727f30f591926751b97b98314d8e59804f54f86ae6fa6a22", - "sha256:caf5eaaf7c68f8d7df269dfbcaf46f48a70ff482bfcebdcc97519671023f2a7d", - "sha256:d48999c4b19b5a0c058c9cd828ff6fc7748390679f6cf9a2ad653a3e802c87d3", - "sha256:d5adc743de91e8e0b13df60deb1b1c285b8effea3d66223afceb14b63c9b05de", - "sha256:dfc118642903a23e309b1da32886bb39a4314147d013e820c86b5fb4cb2e36d0", - "sha256:e594ee43c59ea39ca5c6244667cac9d017a3527febc31f5532ad9135cf7469ec", - "sha256:e78707b751260b42b721507ad7aa60fe4026d7f51c74cca6b9cd8b123ebb633a", - "sha256:ebd8470cc2a3594746ff0513aecbfa2c55ff6f58e6cef2efb1a54eb87c88ffa2", - "sha256:ec726b08a5275d827aa91bb951e68234a4423adb91cf65bc0fcdc0f2777663f7", - "sha256:edf54cac8ee3603f3093616b40a931e8c063969756a4d78a86e82c2fea9659f7", - "sha256:ee152a88a0da527840a426535514b6ed8ac4240eb856b1da92cf48124320e346", - "sha256:f09b3dd6bdeb588de91f853bbb2d6f0ff8ab693485b0c49035eaa510cb4f142e", - "sha256:faa3d12d8811d08d14080a8b7b9caea9a457dc495350166b56df0db4b9909ef5" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==3.7.3" - }, - "matplotlib-inline": { - "hashes": [ - "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311", - "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304" - ], - "markers": "python_version >= '3.5'", - "version": "==0.1.6" - }, - "merge-args": { - "hashes": [ - "sha256:ad37e6c4c7f1eadda5a0564b26ca7d56f8a37f232f5c5c46f9ef93d352757c23", - "sha256:f296a2dd9156dd055e3f540f0fb5784b94933457a4e0e8d0031a763c26bf24a8" - ], - "version": "==0.1.5" - }, - "mistune": { - "hashes": [ - "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205", - "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8" - ], - "markers": "python_version >= '3.7'", - "version": "==3.0.2" - }, - "msgpack": { - "hashes": [ - "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862", - "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d", - "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3", - "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672", - "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0", - "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9", - "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee", - "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46", - "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524", - "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819", - "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc", - "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc", - "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1", - "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82", - "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81", - "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6", - "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d", - "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2", - "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c", - "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87", - "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84", - "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e", - "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95", - "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f", - "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b", - "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93", - "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf", - "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61", - "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c", - "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8", - "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d", - "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c", - "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4", - "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba", - "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415", - "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee", - "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d", - "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9", - "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075", - "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f", - "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7", - "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681", - "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329", - "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1", - "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf", - "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c", - "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5", - "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b", - "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5", - "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e", - "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b", - "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad", - "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd", - "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7", - "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002", - "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc" - ], - "markers": "python_version >= '3.8'", - "version": "==1.0.7" - }, - "mypy-extensions": { - "hashes": [ - "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", - "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" - ], - "markers": "python_version >= '3.5'", - "version": "==1.0.0" - }, - "nbclient": { - "hashes": [ - "sha256:25e861299e5303a0477568557c4045eccc7a34c17fc08e7959558707b9ebe548", - "sha256:f9b179cd4b2d7bca965f900a2ebf0db4a12ebff2f36a711cb66861e4ae158e55" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==0.8.0" - }, - "nbconvert": { - "hashes": [ - "sha256:4bedff08848626be544de193b7594d98a048073f392178008ff4f171f5e21d26", - "sha256:8cf1d95e569730f136feb85e4bba25bdcf3a63fefb122d854ddff6771c0ac933" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==7.10.0" - }, - "nbformat": { - "hashes": [ - "sha256:1c5172d786a41b82bcfd0c23f9e6b6f072e8fb49c39250219e4acfff1efe89e9", - "sha256:5f98b5ba1997dff175e77e0c17d5c10a96eaed2cbd1de3533d1fc35d5e111192" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==5.9.2" - }, - "nest-asyncio": { - "hashes": [ - "sha256:25aa2ca0d2a5b5531956b9e273b45cf664cae2b145101d73b86b199978d48fdb", - "sha256:accda7a339a70599cb08f9dd09a67e0c2ef8d8d6f4c07f96ab203f2ae254e48d" - ], - "markers": "python_version >= '3.5'", - "version": "==1.5.8" - }, - "networkx": { - "hashes": [ - "sha256:4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36", - "sha256:de346335408f84de0eada6ff9fafafff9bcda11f0a0dfaa931133debb146ab61" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==3.1" - }, - "nteract-scrapbook": { - "hashes": [ - "sha256:10ec7a33c32ceae5fdd7fb84260c5184c36c7f2b4f6f7c4679b9761f766e7e35", - "sha256:1a5e2d9d04b2d2ce2d05e430429966411ee765c263e4f9aecfa01807fc3b23b2" - ], - "index": "pypi", - "markers": "python_version >= '3.5'", - "version": "==0.4.2" - }, - "numpy": { - "hashes": [ - "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f", - "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61", - "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7", - "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400", - "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef", - "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2", - "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d", - "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc", - "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835", - "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706", - "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5", - "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4", - "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6", - "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463", - "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a", - "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f", - "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e", - "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e", - "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694", - "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8", - "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64", - "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d", - "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc", - "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254", - "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2", - "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1", - "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810", - "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9" - ], - "markers": "python_version < '3.10'", - "version": "==1.24.4" - }, - "packaging": { - "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" - ], - "markers": "python_version >= '3.7'", - "version": "==23.2" - }, - "pandas": { - "hashes": [ - "sha256:04dbdbaf2e4d46ca8da896e1805bc04eb85caa9a82e259e8eed00254d5e0c682", - "sha256:1168574b036cd8b93abc746171c9b4f1b83467438a5e45909fed645cf8692dbc", - "sha256:1994c789bf12a7c5098277fb43836ce090f1073858c10f9220998ac74f37c69b", - "sha256:258d3624b3ae734490e4d63c430256e716f488c4fcb7c8e9bde2d3aa46c29089", - "sha256:32fca2ee1b0d93dd71d979726b12b61faa06aeb93cf77468776287f41ff8fdc5", - "sha256:37673e3bdf1551b95bf5d4ce372b37770f9529743d2498032439371fc7b7eb26", - "sha256:3ef285093b4fe5058eefd756100a367f27029913760773c8bf1d2d8bebe5d210", - "sha256:5247fb1ba347c1261cbbf0fcfba4a3121fbb4029d95d9ef4dc45406620b25c8b", - "sha256:5ec591c48e29226bcbb316e0c1e9423622bc7a4eaf1ef7c3c9fa1a3981f89641", - "sha256:694888a81198786f0e164ee3a581df7d505024fbb1f15202fc7db88a71d84ebd", - "sha256:69d7f3884c95da3a31ef82b7618af5710dba95bb885ffab339aad925c3e8ce78", - "sha256:6a21ab5c89dcbd57f78d0ae16630b090eec626360085a4148693def5452d8a6b", - "sha256:81af086f4543c9d8bb128328b5d32e9986e0c84d3ee673a2ac6fb57fd14f755e", - "sha256:9e4da0d45e7f34c069fe4d522359df7d23badf83abc1d1cef398895822d11061", - "sha256:9eae3dc34fa1aa7772dd3fc60270d13ced7346fcbcfee017d3132ec625e23bb0", - "sha256:9ee1a69328d5c36c98d8e74db06f4ad518a1840e8ccb94a4ba86920986bb617e", - "sha256:b084b91d8d66ab19f5bb3256cbd5ea661848338301940e17f4492b2ce0801fe8", - "sha256:b9cb1e14fdb546396b7e1b923ffaeeac24e4cedd14266c3497216dd4448e4f2d", - "sha256:ba619e410a21d8c387a1ea6e8a0e49bb42216474436245718d7f2e88a2f8d7c0", - "sha256:c02f372a88e0d17f36d3093a644c73cfc1788e876a7c4bcb4020a77512e2043c", - "sha256:ce0c6f76a0f1ba361551f3e6dceaff06bde7514a374aa43e33b588ec10420183", - "sha256:d9cd88488cceb7635aebb84809d087468eb33551097d600c6dad13602029c2df", - "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8", - "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f", - "sha256:f3421a7afb1a43f7e38e82e844e2bca9a6d793d66c1a7f9f0ff39a795bbc5e02" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==2.0.3" - }, - "pandocfilters": { - "hashes": [ - "sha256:0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38", - "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.5.0" - }, - "papermill": { - "hashes": [ - "sha256:6f8f8a9b06b39677f207c09100c8d386bcf592f0cbbdda9f0f50e81445697627", - "sha256:baa76f0441257d9a25b3ad7c895e761341b94f9a70ca98cf419247fc728932d9" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.4.0" - }, - "parso": { - "hashes": [ - "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0", - "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75" - ], - "markers": "python_version >= '3.6'", - "version": "==0.8.3" - }, - "partd": { - "hashes": [ - "sha256:27e766663d36c161e2827aa3e28541c992f0b9527d3cca047e13fb3acdb989e6", - "sha256:56c25dd49e6fea5727e731203c466c6e092f308d8f0024e199d02f6aa2167f67" - ], - "markers": "python_version >= '3.7'", - "version": "==1.4.1" - }, - "pathspec": { - "hashes": [ - "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20", - "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3" - ], - "markers": "python_version >= '3.7'", - "version": "==0.11.2" - }, - "pendulum": { - "hashes": [ - "sha256:0731f0c661a3cb779d398803655494893c9f581f6488048b3fb629c2342b5394", - "sha256:1245cd0075a3c6d889f581f6325dd8404aca5884dea7223a5566c38aab94642b", - "sha256:29c40a6f2942376185728c9a0347d7c0f07905638c83007e1d262781f1e6953a", - "sha256:2d1619a721df661e506eff8db8614016f0720ac171fe80dda1333ee44e684087", - "sha256:318f72f62e8e23cd6660dbafe1e346950281a9aed144b5c596b2ddabc1d19739", - "sha256:33fb61601083f3eb1d15edeb45274f73c63b3c44a8524703dc143f4212bf3269", - "sha256:3481fad1dc3f6f6738bd575a951d3c15d4b4ce7c82dce37cf8ac1483fde6e8b0", - "sha256:4c9c689747f39d0d02a9f94fcee737b34a5773803a64a5fdb046ee9cac7442c5", - "sha256:7c5ec650cb4bec4c63a89a0242cc8c3cebcec92fcfe937c417ba18277d8560be", - "sha256:94b1fc947bfe38579b28e1cccb36f7e28a15e841f30384b5ad6c5e31055c85d7", - "sha256:9702069c694306297ed362ce7e3c1ef8404ac8ede39f9b28b7c1a7ad8c3959e3", - "sha256:b06a0ca1bfe41c990bbf0c029f0b6501a7f2ec4e38bfec730712015e8860f207", - "sha256:b6c352f4bd32dff1ea7066bd31ad0f71f8d8100b9ff709fb343f3b86cee43efe", - "sha256:c501749fdd3d6f9e726086bf0cd4437281ed47e7bca132ddb522f86a1645d360", - "sha256:c807a578a532eeb226150d5006f156632df2cc8c5693d778324b43ff8c515dd0", - "sha256:db0a40d8bcd27b4fb46676e8eb3c732c67a5a5e6bfab8927028224fbced0b40b", - "sha256:de42ea3e2943171a9e95141f2eecf972480636e8e484ccffaf1e833929e9e052", - "sha256:e95d329384717c7bf627bf27e204bc3b15c8238fa8d9d9781d93712776c14002", - "sha256:f5e236e7730cab1644e1b87aca3d2ff3e375a608542e90fe25685dae46310116", - "sha256:f888f2d2909a414680a29ae74d0592758f2b9fcdee3549887779cd4055e975db", - "sha256:fb53ffa0085002ddd43b6ca61a7b34f2d4d7c3ed66f931fe599e1a531b42af9b" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==2.1.2" - }, - "pexpect": { - "hashes": [ - "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937", - "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c" - ], - "markers": "sys_platform != 'win32'", - "version": "==4.8.0" - }, - "pickleshare": { - "hashes": [ - "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", - "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56" - ], - "version": "==0.7.5" - }, - "pillow": { - "hashes": [ - "sha256:00f438bb841382b15d7deb9a05cc946ee0f2c352653c7aa659e75e592f6fa17d", - "sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de", - "sha256:04f6f6149f266a100374ca3cc368b67fb27c4af9f1cc8cb6306d849dcdf12616", - "sha256:062a1610e3bc258bff2328ec43f34244fcec972ee0717200cb1425214fe5b839", - "sha256:0a026c188be3b443916179f5d04548092e253beb0c3e2ee0a4e2cdad72f66099", - "sha256:0f7c276c05a9767e877a0b4c5050c8bee6a6d960d7f0c11ebda6b99746068c2a", - "sha256:1a8413794b4ad9719346cd9306118450b7b00d9a15846451549314a58ac42219", - "sha256:1ab05f3db77e98f93964697c8efc49c7954b08dd61cff526b7f2531a22410106", - "sha256:1c3ac5423c8c1da5928aa12c6e258921956757d976405e9467c5f39d1d577a4b", - "sha256:1c41d960babf951e01a49c9746f92c5a7e0d939d1652d7ba30f6b3090f27e412", - "sha256:1fafabe50a6977ac70dfe829b2d5735fd54e190ab55259ec8aea4aaea412fa0b", - "sha256:1fb29c07478e6c06a46b867e43b0bcdb241b44cc52be9bc25ce5944eed4648e7", - "sha256:24fadc71218ad2b8ffe437b54876c9382b4a29e030a05a9879f615091f42ffc2", - "sha256:2cdc65a46e74514ce742c2013cd4a2d12e8553e3a2563c64879f7c7e4d28bce7", - "sha256:2ef6721c97894a7aa77723740a09547197533146fba8355e86d6d9a4a1056b14", - "sha256:3b834f4b16173e5b92ab6566f0473bfb09f939ba14b23b8da1f54fa63e4b623f", - "sha256:3d929a19f5469b3f4df33a3df2983db070ebb2088a1e145e18facbc28cae5b27", - "sha256:41f67248d92a5e0a2076d3517d8d4b1e41a97e2df10eb8f93106c89107f38b57", - "sha256:47e5bf85b80abc03be7455c95b6d6e4896a62f6541c1f2ce77a7d2bb832af262", - "sha256:4d0152565c6aa6ebbfb1e5d8624140a440f2b99bf7afaafbdbf6430426497f28", - "sha256:50d08cd0a2ecd2a8657bd3d82c71efd5a58edb04d9308185d66c3a5a5bed9610", - "sha256:61f1a9d247317fa08a308daaa8ee7b3f760ab1809ca2da14ecc88ae4257d6172", - "sha256:6932a7652464746fcb484f7fc3618e6503d2066d853f68a4bd97193a3996e273", - "sha256:7a7e3daa202beb61821c06d2517428e8e7c1aab08943e92ec9e5755c2fc9ba5e", - "sha256:7dbaa3c7de82ef37e7708521be41db5565004258ca76945ad74a8e998c30af8d", - "sha256:7df5608bc38bd37ef585ae9c38c9cd46d7c81498f086915b0f97255ea60c2818", - "sha256:806abdd8249ba3953c33742506fe414880bad78ac25cc9a9b1c6ae97bedd573f", - "sha256:883f216eac8712b83a63f41b76ddfb7b2afab1b74abbb413c5df6680f071a6b9", - "sha256:912e3812a1dbbc834da2b32299b124b5ddcb664ed354916fd1ed6f193f0e2d01", - "sha256:937bdc5a7f5343d1c97dc98149a0be7eb9704e937fe3dc7140e229ae4fc572a7", - "sha256:9882a7451c680c12f232a422730f986a1fcd808da0fd428f08b671237237d651", - "sha256:9a92109192b360634a4489c0c756364c0c3a2992906752165ecb50544c251312", - "sha256:9d7bc666bd8c5a4225e7ac71f2f9d12466ec555e89092728ea0f5c0c2422ea80", - "sha256:a5f63b5a68daedc54c7c3464508d8c12075e56dcfbd42f8c1bf40169061ae666", - "sha256:a646e48de237d860c36e0db37ecaecaa3619e6f3e9d5319e527ccbc8151df061", - "sha256:a89b8312d51715b510a4fe9fc13686283f376cfd5abca8cd1c65e4c76e21081b", - "sha256:a92386125e9ee90381c3369f57a2a50fa9e6aa8b1cf1d9c4b200d41a7dd8e992", - "sha256:ae88931f93214777c7a3aa0a8f92a683f83ecde27f65a45f95f22d289a69e593", - "sha256:afc8eef765d948543a4775f00b7b8c079b3321d6b675dde0d02afa2ee23000b4", - "sha256:b0eb01ca85b2361b09480784a7931fc648ed8b7836f01fb9241141b968feb1db", - "sha256:b1c25762197144e211efb5f4e8ad656f36c8d214d390585d1d21281f46d556ba", - "sha256:b4005fee46ed9be0b8fb42be0c20e79411533d1fd58edabebc0dd24626882cfd", - "sha256:b920e4d028f6442bea9a75b7491c063f0b9a3972520731ed26c83e254302eb1e", - "sha256:baada14941c83079bf84c037e2d8b7506ce201e92e3d2fa0d1303507a8538212", - "sha256:bb40c011447712d2e19cc261c82655f75f32cb724788df315ed992a4d65696bb", - "sha256:c0949b55eb607898e28eaccb525ab104b2d86542a85c74baf3a6dc24002edec2", - "sha256:c9aeea7b63edb7884b031a35305629a7593272b54f429a9869a4f63a1bf04c34", - "sha256:cfe96560c6ce2f4c07d6647af2d0f3c54cc33289894ebd88cfbb3bcd5391e256", - "sha256:d27b5997bdd2eb9fb199982bb7eb6164db0426904020dc38c10203187ae2ff2f", - "sha256:d921bc90b1defa55c9917ca6b6b71430e4286fc9e44c55ead78ca1a9f9eba5f2", - "sha256:e6bf8de6c36ed96c86ea3b6e1d5273c53f46ef518a062464cd7ef5dd2cf92e38", - "sha256:eaed6977fa73408b7b8a24e8b14e59e1668cfc0f4c40193ea7ced8e210adf996", - "sha256:fa1d323703cfdac2036af05191b969b910d8f115cf53093125e4058f62012c9a", - "sha256:fe1e26e1ffc38be097f0ba1d0d07fcade2bcfd1d023cda5b29935ae8052bd793" - ], - "markers": "python_version >= '3.8'", - "version": "==10.1.0" - }, - "pkgutil-resolve-name": { - "hashes": [ - "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174", - "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e" - ], - "markers": "python_version < '3.9'", - "version": "==1.3.10" - }, - "platformdirs": { - "hashes": [ - "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3", - "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e" - ], - "markers": "python_version >= '3.7'", - "version": "==3.11.0" - }, - "prefect": { - "hashes": [ - "sha256:179f179849286bb8dc0309c8718a7815e6e5fcc016398d2aea45e16fa0e3471b", - "sha256:a838d427a88845b13279b89b925e2b6acde5ff2bb090c5480617bc6047a808a8" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.4.1" - }, - "prompt-toolkit": { - "hashes": [ - "sha256:04505ade687dc26dc4284b1ad19a83be2f2afe83e7a828ace0c72f3a1df72aac", - "sha256:9dffbe1d8acf91e3de75f3b544e4842382fc06c6babe903ac9acb74dc6e08d88" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.0.39" - }, - "psutil": { - "hashes": [ - "sha256:10e8c17b4f898d64b121149afb136c53ea8b68c7531155147867b7b1ac9e7e28", - "sha256:18cd22c5db486f33998f37e2bb054cc62fd06646995285e02a51b1e08da97017", - "sha256:3ebf2158c16cc69db777e3c7decb3c0f43a7af94a60d72e87b2823aebac3d602", - "sha256:51dc3d54607c73148f63732c727856f5febec1c7c336f8f41fcbd6315cce76ac", - "sha256:6e5fb8dc711a514da83098bc5234264e551ad980cec5f85dabf4d38ed6f15e9a", - "sha256:70cb3beb98bc3fd5ac9ac617a327af7e7f826373ee64c80efd4eb2856e5051e9", - "sha256:748c9dd2583ed86347ed65d0035f45fa8c851e8d90354c122ab72319b5f366f4", - "sha256:91ecd2d9c00db9817a4b4192107cf6954addb5d9d67a969a4f436dbc9200f88c", - "sha256:92e0cc43c524834af53e9d3369245e6cc3b130e78e26100d1f63cdb0abeb3d3c", - "sha256:a6f01f03bf1843280f4ad16f4bde26b817847b4c1a0db59bf6419807bc5ce05c", - "sha256:c69596f9fc2f8acd574a12d5f8b7b1ba3765a641ea5d60fb4736bf3c08a8214a", - "sha256:ca2780f5e038379e520281e4c032dddd086906ddff9ef0d1b9dcf00710e5071c", - "sha256:daecbcbd29b289aac14ece28eca6a3e60aa361754cf6da3dfb20d4d32b6c7f57", - "sha256:e4b92ddcd7dd4cdd3f900180ea1e104932c7bce234fb88976e2a3b296441225a", - "sha256:fb8a697f11b0f5994550555fcfe3e69799e5b060c8ecf9e2f75c69302cc35c0d", - "sha256:ff18b8d1a784b810df0b0fff3bcb50ab941c3b8e2c8de5726f9c71c601c611aa" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==5.9.6" - }, - "psycopg2-binary": { - "hashes": [ - "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9", - "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77", - "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e", - "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84", - "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3", - "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2", - "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67", - "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876", - "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152", - "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f", - "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a", - "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6", - "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503", - "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f", - "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493", - "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996", - "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f", - "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e", - "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59", - "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94", - "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7", - "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682", - "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420", - "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae", - "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291", - "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe", - "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980", - "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93", - "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692", - "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119", - "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716", - "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472", - "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b", - "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2", - "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc", - "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c", - "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5", - "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab", - "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984", - "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9", - "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf", - "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0", - "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f", - "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212", - "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb", - "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be", - "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90", - "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041", - "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7", - "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860", - "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245", - "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27", - "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417", - "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359", - "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202", - "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0", - "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7", - "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba", - "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1", - "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd", - "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07", - "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98", - "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55", - "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d", - "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972", - "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f", - "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e", - "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26", - "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957", - "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53", - "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.9.9" - }, - "ptyprocess": { - "hashes": [ - "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", - "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220" - ], - "markers": "os_name != 'nt'", - "version": "==0.7.0" - }, - "pure-eval": { - "hashes": [ - "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350", - "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3" - ], - "version": "==0.2.2" - }, - "pyarrow": { - "hashes": [ - "sha256:09552dad5cf3de2dc0aba1c7c4b470754c69bd821f5faafc3d774bedc3b04bb7", - "sha256:0f6eff839a9e40e9c5610d3ff8c5bdd2f10303408312caf4c8003285d0b49565", - "sha256:1afcc2c33f31f6fb25c92d50a86b7a9f076d38acbcb6f9e74349636109550148", - "sha256:3896ae6c205d73ad192d2fc1489cd0edfab9f12867c85b4c277af4d37383c18c", - "sha256:47663efc9c395e31d09c6aacfa860f4473815ad6804311c5433f7085415d62a7", - "sha256:51be67e29f3cfcde263a113c28e96aa04362ed8229cb7c6e5f5c719003659d33", - "sha256:588f0d2da6cf1b1680974d63be09a6530fd1bd825dc87f76e162404779a157dc", - "sha256:6241afd72b628787b4abea39e238e3ff9f34165273fad306c7acf780dd850956", - "sha256:6647444b21cb5e68b593b970b2a9a07748dd74ea457c7dadaa15fd469c48ada1", - "sha256:68fcd2dc1b7d9310b29a15949cdd0cb9bc34b6de767aff979ebf546020bf0ba0", - "sha256:69b6f9a089d116a82c3ed819eea8fe67dae6105f0d81eaf0fdd5e60d0c6e0944", - "sha256:70fa38cdc66b2fc1349a082987f2b499d51d072faaa6b600f71931150de2e0e3", - "sha256:83333726e83ed44b0ac94d8d7a21bbdee4a05029c3b1e8db58a863eec8fd8a33", - "sha256:868a073fd0ff6468ae7d869b5fc1f54de5c4255b37f44fb890385eb68b68f95d", - "sha256:8b30a27f1cddf5c6efcb67e598d7823a1e253d743d92ac32ec1eb4b6a1417867", - "sha256:aac0ae0146a9bfa5e12d87dda89d9ef7c57a96210b899459fc2f785303dcbb67", - "sha256:ab1268db81aeb241200e321e220e7cd769762f386f92f61b898352dd27e402ce", - "sha256:b9ba6b6d34bd2563345488cf444510588ea42ad5613df3b3509f48eb80250afd", - "sha256:c51afd87c35c8331b56f796eff954b9c7f8d4b7fef5903daf4e05fcf017d23a8", - "sha256:cd57b13a6466822498238877892a9b287b0a58c2e81e4bdb0b596dbb151cbb73", - "sha256:d00d374a5625beeb448a7fa23060df79adb596074beb3ddc1838adb647b6ef09", - "sha256:d1b4e7176443d12610874bb84d0060bf080f000ea9ed7c84b2801df851320295", - "sha256:d7759994217c86c161c6a8060509cfdf782b952163569606bb373828afdd82e8", - "sha256:dc6fd330fd574c51d10638e63c0d00ab456498fc804c9d01f2a61b9264f2c5b2", - "sha256:e3ad79455c197a36eefbd90ad4aa832bece7f830a64396c15c61a0985e337287", - "sha256:e66442e084979a97bb66939e18f7b8709e4ac5f887e636aba29486ffbf373763", - "sha256:ee7490f0f3f16a6c38f8c680949551053c8194e68de5046e6c288e396dccee80", - "sha256:f8ce69f7bf01de2e2764e14df45b8404fc6f1a5ed9871e8e08a12169f87b7a26", - "sha256:fda7857e35993673fcda603c07d43889fca60a5b254052a462653f8656c64f44" - ], - "markers": "python_version >= '3.8'", - "version": "==13.0.0" - }, - "pygments": { - "hashes": [ - "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692", - "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29" - ], - "markers": "python_version >= '3.7'", - "version": "==2.16.1" - }, - "pyjwt": { - "hashes": [ - "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de", - "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320" - ], - "markers": "python_version >= '3.7'", - "version": "==2.8.0" - }, - "pyparsing": { - "hashes": [ - "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb", - "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db" - ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.1.1" - }, - "pyproj": { - "hashes": [ - "sha256:052c49fce8b5d55943a35c36ccecb87350c68b48ba95bc02a789770c374ef819", - "sha256:1507138ea28bf2134d31797675380791cc1a7156a3aeda484e65a78a4aba9b62", - "sha256:1798ff7d65d9057ebb2d017ffe8403268b8452f24d0428b2140018c25c7fa1bc", - "sha256:1b7c2113c4d11184a238077ec85e31eda1dcc58ffeb9a4429830e0a7036e787d", - "sha256:2b708fd43453b985642b737d4a6e7f1d6a0ab1677ffa4e14cc258537b49224b0", - "sha256:385b0341861d3ebc8cad98337a738821dcb548d465576527399f4955ca24b6ed", - "sha256:38862fe07316ae12b79d82d298e390973a4f00b684f3c2d037238e20e00610ba", - "sha256:5674923351e76222e2c10c58b5e1ac119d7a46b270d822c463035971b06f724b", - "sha256:5c4b85ac10d733c42d73a2e6261c8d6745bf52433a31848dd1b6561c9a382da3", - "sha256:61e4ad57d89b03a7b173793b31bca8ee110112cde1937ef0f42a70b9120c827d", - "sha256:621d78a9d8bf4d06e08bef2471021fbcb1a65aa629ad4a20c22e521ce729cc20", - "sha256:6475ce653880938468a1a1b7321267243909e34b972ba9e53d5982c41d555918", - "sha256:6a87b419a2a352413fbf759ecb66da9da50bd19861c8f26db6a25439125b27b9", - "sha256:6f316a66031a14e9c5a88c91f8b77aa97f5454895674541ed6ab630b682be35d", - "sha256:71b65f2a38cd9e16883dbb0f8ae82bdf8f6b79b1b02975c78483ab8428dbbf2f", - "sha256:73f7960a97225812f9b1d7aeda5fb83812f38de9441e3476fcc8abb3e2b2f4de", - "sha256:7572983134e310e0ca809c63f1722557a040fe9443df5f247bf11ba887eb1229", - "sha256:788a5dadb532644a64efe0f5f01bf508c821eb7e984f13a677d56002f1e8a67a", - "sha256:7bdd2021bb6f7f346bfe1d2a358aa109da017d22c4704af2d994e7c7ee0a7a53", - "sha256:8fe6bb1b68a35d07378d38be77b5b2f8dd2bea5910c957bfcc7bee55988d3910", - "sha256:97ed199033c2c770e7eea2ef80ff5e6413426ec2d7ec985b869792f04ab95d05", - "sha256:9859d1591c1863414d875ae0759e72c2cffc01ab989dc64137fbac572cc81bf6", - "sha256:a730f5b4c98c8a0f312437873e6e34dbd4cc6dc23d5afd91a6691c62724b1f68", - "sha256:b60d93a200639e8367c6542a964fd0aa2dbd152f256c1831dc18cd5aa470fb8a", - "sha256:b752b7d9c4b08181c7e8c0d9c7f277cbefff42227f34d3310696a87c863d9dd3", - "sha256:b937215bfbaf404ec8f03ca741fc3f9f2c4c2c5590a02ccddddd820ae3c71331", - "sha256:c02742ef3d846401861a878a61ef7ad911ea7539d6cc4619ddb52dbdf7b45aee", - "sha256:cd5e2b6aa255023c4acd0b977590f1f7cc801ba21b4d806fcf6dfac3474ebb83", - "sha256:d711517a8487ef3245b08dc82f781a906df9abb3b6cb0ce0486f0eeb823ca570", - "sha256:d9a024370e917c899bff9171f03ea6079deecdc7482a146a2c565f3b9df134ea", - "sha256:e08db25b61cf024648d55973cc3d1c3f1d0818fabf594d5f5a8e2318103d2aa0", - "sha256:e97573de0ab3bbbcb4c7748bc41f4ceb6da10b45d35b1a294b5820701e7c25f0", - "sha256:eccb417b91d0be27805dfc97550bfb8b7db94e9fe1db5ebedb98f5b88d601323", - "sha256:f7c2f4d9681e810cf40239caaca00079930a6d9ee6591139b88d592d36051d82", - "sha256:fde5ece4d2436b5a57c8f5f97b49b5de06a856d03959f836c957d3e609f2de7e" - ], - "markers": "python_version >= '3.8'", - "version": "==3.5.0" - }, - "python-box": { - "hashes": [ - "sha256:0036fd47d388deaca8ebd65aea905f88ee6ef91d1d8ce34898b66f1824afbe80", - "sha256:1b3f346e332dba16df0b0543d319d9e7ce07d93e5ae152175302894352aa2d28", - "sha256:24c4ec0ee0278f66321100aaa9c615413da27a14ff43d376a2a3b4665e1d9494", - "sha256:2a3df244a5a79ac8f8447b5d11b5be0f2747d7b141cb2866060081ae9b53cc50", - "sha256:4ea13c98e05a3ec0ff26f254986a17290b69b5ade209fad081fd628f8fcfaa08", - "sha256:63b609555554d7a9d4b6e725f8e78ef1717c67e7d386200e03422ad612338df8", - "sha256:81ed1ec0f0ff2370227fc07277c5baca46d190a4747631bad7eb6ab1630fb7d9", - "sha256:8891735b4148e84d348c6eadd2f127152f751c9603e35d43a1f496183a291ac4", - "sha256:a0f1333c42e81529b6f68c192050df9d4505b803be7ac47f114036b98707f7cf", - "sha256:aabf8b9ae5dbc8ba431d8cbe0d4cfe737a25d52d68b0f5f2ff34915c21a2c1db", - "sha256:c046608337e723ae4de3206db5d1e1202ed166da2dfdc70c1f9361e72ace5633", - "sha256:d95e5eec4fc8f3fc5c9cc7347fc2eb4f9187c853d34c90b1658d1eff96cd4eac", - "sha256:f76b5b7f0cdc07bfdd4200dc24e6e33189bb2ae322137a2b7110fd41891a3157", - "sha256:f9266795e9c233874fb5b34fa994054b4fb0371881678e6ec45aec17fc95feac" - ], - "markers": "python_version >= '3.8'", - "version": "==7.1.1" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "python-slugify": { - "hashes": [ - "sha256:70ca6ea68fe63ecc8fa4fcf00ae651fc8a5d02d93dcd12ae6d4fc7ca46c4d395", - "sha256:ce0d46ddb668b3be82f4ed5e503dbc33dd815d83e2eb6824211310d3fb172a27" - ], - "markers": "python_version >= '3.7'", - "version": "==8.0.1" - }, - "pytz": { - "hashes": [ - "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b", - "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7" - ], - "version": "==2023.3.post1" - }, - "pytzdata": { - "hashes": [ - "sha256:3efa13b335a00a8de1d345ae41ec78dd11c9f8807f522d39850f2dd828681540", - "sha256:e1e14750bcf95016381e4d472bad004eef710f2d6417240904070b3d6654485f" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2020.1" - }, - "pyyaml": { - "hashes": [ - "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", - "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", - "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", - "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", - "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", - "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", - "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", - "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", - "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", - "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", - "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", - "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", - "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", - "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", - "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", - "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", - "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", - "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", - "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", - "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", - "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", - "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", - "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", - "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", - "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", - "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", - "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", - "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", - "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", - "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", - "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", - "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", - "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", - "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", - "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", - "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", - "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", - "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", - "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", - "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", - "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", - "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", - "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", - "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", - "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", - "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", - "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", - "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", - "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", - "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==6.0.1" - }, - "pyzmq": { - "hashes": [ - "sha256:019e59ef5c5256a2c7378f2fb8560fc2a9ff1d315755204295b2eab96b254d0a", - "sha256:034239843541ef7a1aee0c7b2cb7f6aafffb005ede965ae9cbd49d5ff4ff73cf", - "sha256:03b3f49b57264909aacd0741892f2aecf2f51fb053e7d8ac6767f6c700832f45", - "sha256:047a640f5c9c6ade7b1cc6680a0e28c9dd5a0825135acbd3569cc96ea00b2505", - "sha256:04ccbed567171579ec2cebb9c8a3e30801723c575601f9a990ab25bcac6b51e2", - "sha256:057e824b2aae50accc0f9a0570998adc021b372478a921506fddd6c02e60308e", - "sha256:11baebdd5fc5b475d484195e49bae2dc64b94a5208f7c89954e9e354fc609d8f", - "sha256:11c1d2aed9079c6b0c9550a7257a836b4a637feb334904610f06d70eb44c56d2", - "sha256:11d58723d44d6ed4dd677c5615b2ffb19d5c426636345567d6af82be4dff8a55", - "sha256:12720a53e61c3b99d87262294e2b375c915fea93c31fc2336898c26d7aed34cd", - "sha256:17ef5f01d25b67ca8f98120d5fa1d21efe9611604e8eb03a5147360f517dd1e2", - "sha256:18d43df3f2302d836f2a56f17e5663e398416e9dd74b205b179065e61f1a6edf", - "sha256:1a5d26fe8f32f137e784f768143728438877d69a586ddeaad898558dc971a5ae", - "sha256:1af379b33ef33757224da93e9da62e6471cf4a66d10078cf32bae8127d3d0d4a", - "sha256:1ccf825981640b8c34ae54231b7ed00271822ea1c6d8ba1090ebd4943759abf5", - "sha256:21eb4e609a154a57c520e3d5bfa0d97e49b6872ea057b7c85257b11e78068222", - "sha256:2243700cc5548cff20963f0ca92d3e5e436394375ab8a354bbea2b12911b20b0", - "sha256:255ca2b219f9e5a3a9ef3081512e1358bd4760ce77828e1028b818ff5610b87b", - "sha256:259c22485b71abacdfa8bf79720cd7bcf4b9d128b30ea554f01ae71fdbfdaa23", - "sha256:25f0e6b78220aba09815cd1f3a32b9c7cb3e02cb846d1cfc526b6595f6046618", - "sha256:273bc3959bcbff3f48606b28229b4721716598d76b5aaea2b4a9d0ab454ec062", - "sha256:292fe3fc5ad4a75bc8df0dfaee7d0babe8b1f4ceb596437213821f761b4589f9", - "sha256:2ca57a5be0389f2a65e6d3bb2962a971688cbdd30b4c0bd188c99e39c234f414", - "sha256:2d163a18819277e49911f7461567bda923461c50b19d169a062536fffe7cd9d2", - "sha256:2d81f1ddae3858b8299d1da72dd7d19dd36aab654c19671aa8a7e7fb02f6638a", - "sha256:2f957ce63d13c28730f7fd6b72333814221c84ca2421298f66e5143f81c9f91f", - "sha256:330f9e188d0d89080cde66dc7470f57d1926ff2fb5576227f14d5be7ab30b9fa", - "sha256:34c850ce7976d19ebe7b9d4b9bb8c9dfc7aac336c0958e2651b88cbd46682123", - "sha256:35b5ab8c28978fbbb86ea54958cd89f5176ce747c1fb3d87356cf698048a7790", - "sha256:3669cf8ee3520c2f13b2e0351c41fea919852b220988d2049249db10046a7afb", - "sha256:381469297409c5adf9a0e884c5eb5186ed33137badcbbb0560b86e910a2f1e76", - "sha256:3d0a409d3b28607cc427aa5c30a6f1e4452cc44e311f843e05edb28ab5e36da0", - "sha256:44e58a0554b21fc662f2712814a746635ed668d0fbc98b7cb9d74cb798d202e6", - "sha256:458dea649f2f02a0b244ae6aef8dc29325a2810aa26b07af8374dc2a9faf57e3", - "sha256:48e466162a24daf86f6b5ca72444d2bf39a5e58da5f96370078be67c67adc978", - "sha256:49d238cf4b69652257db66d0c623cd3e09b5d2e9576b56bc067a396133a00d4a", - "sha256:4ca1ed0bb2d850aa8471387882247c68f1e62a4af0ce9c8a1dbe0d2bf69e41fb", - "sha256:52533489f28d62eb1258a965f2aba28a82aa747202c8fa5a1c7a43b5db0e85c1", - "sha256:548d6482dc8aadbe7e79d1b5806585c8120bafa1ef841167bc9090522b610fa6", - "sha256:5619f3f5a4db5dbb572b095ea3cb5cc035335159d9da950830c9c4db2fbb6995", - "sha256:57459b68e5cd85b0be8184382cefd91959cafe79ae019e6b1ae6e2ba8a12cda7", - "sha256:5a34d2395073ef862b4032343cf0c32a712f3ab49d7ec4f42c9661e0294d106f", - "sha256:61706a6b6c24bdece85ff177fec393545a3191eeda35b07aaa1458a027ad1304", - "sha256:724c292bb26365659fc434e9567b3f1adbdb5e8d640c936ed901f49e03e5d32e", - "sha256:73461eed88a88c866656e08f89299720a38cb4e9d34ae6bf5df6f71102570f2e", - "sha256:76705c9325d72a81155bb6ab48d4312e0032bf045fb0754889133200f7a0d849", - "sha256:76c1c8efb3ca3a1818b837aea423ff8a07bbf7aafe9f2f6582b61a0458b1a329", - "sha256:77a41c26205d2353a4c94d02be51d6cbdf63c06fbc1295ea57dad7e2d3381b71", - "sha256:79986f3b4af059777111409ee517da24a529bdbd46da578b33f25580adcff728", - "sha256:7cff25c5b315e63b07a36f0c2bab32c58eafbe57d0dce61b614ef4c76058c115", - "sha256:7f7e58effd14b641c5e4dec8c7dab02fb67a13df90329e61c869b9cc607ef752", - "sha256:820c4a08195a681252f46926de10e29b6bbf3e17b30037bd4250d72dd3ddaab8", - "sha256:87e34f31ca8f168c56d6fbf99692cc8d3b445abb5bfd08c229ae992d7547a92a", - "sha256:8f03d3f0d01cb5a018debeb412441996a517b11c5c17ab2001aa0597c6d6882c", - "sha256:90f26dc6d5f241ba358bef79be9ce06de58d477ca8485e3291675436d3827cf8", - "sha256:955215ed0604dac5b01907424dfa28b40f2b2292d6493445dd34d0dfa72586a8", - "sha256:985bbb1316192b98f32e25e7b9958088431d853ac63aca1d2c236f40afb17c83", - "sha256:a382372898a07479bd34bda781008e4a954ed8750f17891e794521c3e21c2e1c", - "sha256:a882ac0a351288dd18ecae3326b8a49d10c61a68b01419f3a0b9a306190baf69", - "sha256:aa8d6cdc8b8aa19ceb319aaa2b660cdaccc533ec477eeb1309e2a291eaacc43a", - "sha256:abc719161780932c4e11aaebb203be3d6acc6b38d2f26c0f523b5b59d2fc1996", - "sha256:abf34e43c531bbb510ae7e8f5b2b1f2a8ab93219510e2b287a944432fad135f3", - "sha256:ade6d25bb29c4555d718ac6d1443a7386595528c33d6b133b258f65f963bb0f6", - "sha256:afea96f64efa98df4da6958bae37f1cbea7932c35878b185e5982821bc883369", - "sha256:b1579413ae492b05de5a6174574f8c44c2b9b122a42015c5292afa4be2507f28", - "sha256:b3451108ab861040754fa5208bca4a5496c65875710f76789a9ad27c801a0075", - "sha256:b9af3757495c1ee3b5c4e945c1df7be95562277c6e5bccc20a39aec50f826cd0", - "sha256:bc16ac425cc927d0a57d242589f87ee093884ea4804c05a13834d07c20db203c", - "sha256:c2910967e6ab16bf6fbeb1f771c89a7050947221ae12a5b0b60f3bca2ee19bca", - "sha256:c2b92812bd214018e50b6380ea3ac0c8bb01ac07fcc14c5f86a5bb25e74026e9", - "sha256:c2f20ce161ebdb0091a10c9ca0372e023ce24980d0e1f810f519da6f79c60800", - "sha256:c56d748ea50215abef7030c72b60dd723ed5b5c7e65e7bc2504e77843631c1a6", - "sha256:c7c133e93b405eb0d36fa430c94185bdd13c36204a8635470cccc200723c13bb", - "sha256:c9c6c9b2c2f80747a98f34ef491c4d7b1a8d4853937bb1492774992a120f475d", - "sha256:cbc8df5c6a88ba5ae385d8930da02201165408dde8d8322072e3e5ddd4f68e22", - "sha256:cff084c6933680d1f8b2f3b4ff5bbb88538a4aac00d199ac13f49d0698727ecb", - "sha256:d2045d6d9439a0078f2a34b57c7b18c4a6aef0bee37f22e4ec9f32456c852c71", - "sha256:d20a0ddb3e989e8807d83225a27e5c2eb2260eaa851532086e9e0fa0d5287d83", - "sha256:d457aed310f2670f59cc5b57dcfced452aeeed77f9da2b9763616bd57e4dbaae", - "sha256:d89528b4943d27029a2818f847c10c2cecc79fa9590f3cb1860459a5be7933eb", - "sha256:db0b2af416ba735c6304c47f75d348f498b92952f5e3e8bff449336d2728795d", - "sha256:deee9ca4727f53464daf089536e68b13e6104e84a37820a88b0a057b97bba2d2", - "sha256:df27ffddff4190667d40de7beba4a950b5ce78fe28a7dcc41d6f8a700a80a3c0", - "sha256:e0c95ddd4f6e9fca4e9e3afaa4f9df8552f0ba5d1004e89ef0a68e1f1f9807c7", - "sha256:e1c1be77bc5fb77d923850f82e55a928f8638f64a61f00ff18a67c7404faf008", - "sha256:e1ffa1c924e8c72778b9ccd386a7067cddf626884fd8277f503c48bb5f51c762", - "sha256:e2400a94f7dd9cb20cd012951a0cbf8249e3d554c63a9c0cdfd5cbb6c01d2dec", - "sha256:e61f091c3ba0c3578411ef505992d356a812fb200643eab27f4f70eed34a29ef", - "sha256:e8a701123029cc240cea61dd2d16ad57cab4691804143ce80ecd9286b464d180", - "sha256:eadbefd5e92ef8a345f0525b5cfd01cf4e4cc651a2cffb8f23c0dd184975d787", - "sha256:f32260e556a983bc5c7ed588d04c942c9a8f9c2e99213fec11a031e316874c7e", - "sha256:f8115e303280ba09f3898194791a153862cbf9eef722ad8f7f741987ee2a97c7", - "sha256:fedbdc753827cf014c01dbbee9c3be17e5a208dcd1bf8641ce2cd29580d1f0d4" - ], - "markers": "python_version >= '3.6'", - "version": "==25.1.1" - }, - "referencing": { - "hashes": [ - "sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf", - "sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0" - ], - "markers": "python_version >= '3.8'", - "version": "==0.30.2" - }, - "requests": { - "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" - ], - "markers": "python_version >= '3.7'", - "version": "==2.31.0" - }, - "rpds-py": { - "hashes": [ - "sha256:023574366002bf1bd751ebaf3e580aef4a468b3d3c216d2f3f7e16fdabd885ed", - "sha256:031f76fc87644a234883b51145e43985aa2d0c19b063e91d44379cd2786144f8", - "sha256:052a832078943d2b2627aea0d19381f607fe331cc0eb5df01991268253af8417", - "sha256:0699ab6b8c98df998c3eacf51a3b25864ca93dab157abe358af46dc95ecd9801", - "sha256:0713631d6e2d6c316c2f7b9320a34f44abb644fc487b77161d1724d883662e31", - "sha256:0774a46b38e70fdde0c6ded8d6d73115a7c39d7839a164cc833f170bbf539116", - "sha256:0898173249141ee99ffcd45e3829abe7bcee47d941af7434ccbf97717df020e5", - "sha256:09586f51a215d17efdb3a5f090d7cbf1633b7f3708f60a044757a5d48a83b393", - "sha256:102eac53bb0bf0f9a275b438e6cf6904904908562a1463a6fc3323cf47d7a532", - "sha256:10f32b53f424fc75ff7b713b2edb286fdbfc94bf16317890260a81c2c00385dc", - "sha256:150eec465dbc9cbca943c8e557a21afdcf9bab8aaabf386c44b794c2f94143d2", - "sha256:1d7360573f1e046cb3b0dceeb8864025aa78d98be4bb69f067ec1c40a9e2d9df", - "sha256:1f36a9d751f86455dc5278517e8b65580eeee37d61606183897f122c9e51cef3", - "sha256:24656dc36f866c33856baa3ab309da0b6a60f37d25d14be916bd3e79d9f3afcf", - "sha256:25860ed5c4e7f5e10c496ea78af46ae8d8468e0be745bd233bab9ca99bfd2647", - "sha256:26857f0f44f0e791f4a266595a7a09d21f6b589580ee0585f330aaccccb836e3", - "sha256:2bb2e4826be25e72013916eecd3d30f66fd076110de09f0e750163b416500721", - "sha256:2f6da6d842195fddc1cd34c3da8a40f6e99e4a113918faa5e60bf132f917c247", - "sha256:30adb75ecd7c2a52f5e76af50644b3e0b5ba036321c390b8e7ec1bb2a16dd43c", - "sha256:3339eca941568ed52d9ad0f1b8eb9fe0958fa245381747cecf2e9a78a5539c42", - "sha256:34ad87a831940521d462ac11f1774edf867c34172010f5390b2f06b85dcc6014", - "sha256:3777cc9dea0e6c464e4b24760664bd8831738cc582c1d8aacf1c3f546bef3f65", - "sha256:3953c6926a63f8ea5514644b7afb42659b505ece4183fdaaa8f61d978754349e", - "sha256:3c4eff26eddac49d52697a98ea01b0246e44ca82ab09354e94aae8823e8bda02", - "sha256:40578a6469e5d1df71b006936ce95804edb5df47b520c69cf5af264d462f2cbb", - "sha256:40f93086eef235623aa14dbddef1b9fb4b22b99454cb39a8d2e04c994fb9868c", - "sha256:4134aa2342f9b2ab6c33d5c172e40f9ef802c61bb9ca30d21782f6e035ed0043", - "sha256:442626328600bde1d09dc3bb00434f5374948838ce75c41a52152615689f9403", - "sha256:4a5ee600477b918ab345209eddafde9f91c0acd931f3776369585a1c55b04c57", - "sha256:4ce5a708d65a8dbf3748d2474b580d606b1b9f91b5c6ab2a316e0b0cf7a4ba50", - "sha256:516a611a2de12fbea70c78271e558f725c660ce38e0006f75139ba337d56b1f6", - "sha256:52c215eb46307c25f9fd2771cac8135d14b11a92ae48d17968eda5aa9aaf5071", - "sha256:53c43e10d398e365da2d4cc0bcaf0854b79b4c50ee9689652cdc72948e86f487", - "sha256:5752b761902cd15073a527b51de76bbae63d938dc7c5c4ad1e7d8df10e765138", - "sha256:5e8a78bd4879bff82daef48c14d5d4057f6856149094848c3ed0ecaf49f5aec2", - "sha256:5ed505ec6305abd2c2c9586a7b04fbd4baf42d4d684a9c12ec6110deefe2a063", - "sha256:5ee97c683eaface61d38ec9a489e353d36444cdebb128a27fe486a291647aff6", - "sha256:61fa268da6e2e1cd350739bb61011121fa550aa2545762e3dc02ea177ee4de35", - "sha256:64ccc28683666672d7c166ed465c09cee36e306c156e787acef3c0c62f90da5a", - "sha256:66414dafe4326bca200e165c2e789976cab2587ec71beb80f59f4796b786a238", - "sha256:68fe9199184c18d997d2e4293b34327c0009a78599ce703e15cd9a0f47349bba", - "sha256:6a555ae3d2e61118a9d3e549737bb4a56ff0cec88a22bd1dfcad5b4e04759175", - "sha256:6bdc11f9623870d75692cc33c59804b5a18d7b8a4b79ef0b00b773a27397d1f6", - "sha256:6cf4393c7b41abbf07c88eb83e8af5013606b1cdb7f6bc96b1b3536b53a574b8", - "sha256:6eef672de005736a6efd565577101277db6057f65640a813de6c2707dc69f396", - "sha256:734c41f9f57cc28658d98270d3436dba65bed0cfc730d115b290e970150c540d", - "sha256:73e0a78a9b843b8c2128028864901f55190401ba38aae685350cf69b98d9f7c9", - "sha256:775049dfa63fb58293990fc59473e659fcafd953bba1d00fc5f0631a8fd61977", - "sha256:7854a207ef77319ec457c1eb79c361b48807d252d94348305db4f4b62f40f7f3", - "sha256:78ca33811e1d95cac8c2e49cb86c0fb71f4d8409d8cbea0cb495b6dbddb30a55", - "sha256:79edd779cfc46b2e15b0830eecd8b4b93f1a96649bcb502453df471a54ce7977", - "sha256:7bf347b495b197992efc81a7408e9a83b931b2f056728529956a4d0858608b80", - "sha256:7fde6d0e00b2fd0dbbb40c0eeec463ef147819f23725eda58105ba9ca48744f4", - "sha256:81de24a1c51cfb32e1fbf018ab0bdbc79c04c035986526f76c33e3f9e0f3356c", - "sha256:879fb24304ead6b62dbe5034e7b644b71def53c70e19363f3c3be2705c17a3b4", - "sha256:8e7f2219cb72474571974d29a191714d822e58be1eb171f229732bc6fdedf0ac", - "sha256:9164ec8010327ab9af931d7ccd12ab8d8b5dc2f4c6a16cbdd9d087861eaaefa1", - "sha256:945eb4b6bb8144909b203a88a35e0a03d22b57aefb06c9b26c6e16d72e5eb0f0", - "sha256:99a57006b4ec39dbfb3ed67e5b27192792ffb0553206a107e4aadb39c5004cd5", - "sha256:9e9184fa6c52a74a5521e3e87badbf9692549c0fcced47443585876fcc47e469", - "sha256:9ff93d3aedef11f9c4540cf347f8bb135dd9323a2fc705633d83210d464c579d", - "sha256:a360cfd0881d36c6dc271992ce1eda65dba5e9368575663de993eeb4523d895f", - "sha256:a5d7ed104d158c0042a6a73799cf0eb576dfd5fc1ace9c47996e52320c37cb7c", - "sha256:ac17044876e64a8ea20ab132080ddc73b895b4abe9976e263b0e30ee5be7b9c2", - "sha256:ad857f42831e5b8d41a32437f88d86ead6c191455a3499c4b6d15e007936d4cf", - "sha256:b2039f8d545f20c4e52713eea51a275e62153ee96c8035a32b2abb772b6fc9e5", - "sha256:b455492cab07107bfe8711e20cd920cc96003e0da3c1f91297235b1603d2aca7", - "sha256:b4a9fe992887ac68256c930a2011255bae0bf5ec837475bc6f7edd7c8dfa254e", - "sha256:b5a53f5998b4bbff1cb2e967e66ab2addc67326a274567697379dd1e326bded7", - "sha256:b788276a3c114e9f51e257f2a6f544c32c02dab4aa7a5816b96444e3f9ffc336", - "sha256:bddd4f91eede9ca5275e70479ed3656e76c8cdaaa1b354e544cbcf94c6fc8ac4", - "sha256:c0503c5b681566e8b722fe8c4c47cce5c7a51f6935d5c7012c4aefe952a35eed", - "sha256:c1b3cd23d905589cb205710b3988fc8f46d4a198cf12862887b09d7aaa6bf9b9", - "sha256:c48f3fbc3e92c7dd6681a258d22f23adc2eb183c8cb1557d2fcc5a024e80b094", - "sha256:c63c3ef43f0b3fb00571cff6c3967cc261c0ebd14a0a134a12e83bdb8f49f21f", - "sha256:c6c45a2d2b68c51fe3d9352733fe048291e483376c94f7723458cfd7b473136b", - "sha256:caa1afc70a02645809c744eefb7d6ee8fef7e2fad170ffdeacca267fd2674f13", - "sha256:cc435d059f926fdc5b05822b1be4ff2a3a040f3ae0a7bbbe672babb468944722", - "sha256:cf693eb4a08eccc1a1b636e4392322582db2a47470d52e824b25eca7a3977b53", - "sha256:cf71343646756a072b85f228d35b1d7407da1669a3de3cf47f8bbafe0c8183a4", - "sha256:d08f63561c8a695afec4975fae445245386d645e3e446e6f260e81663bfd2e38", - "sha256:d29ddefeab1791e3c751e0189d5f4b3dbc0bbe033b06e9c333dca1f99e1d523e", - "sha256:d7f5e15c953ace2e8dde9824bdab4bec50adb91a5663df08d7d994240ae6fa31", - "sha256:d858532212f0650be12b6042ff4378dc2efbb7792a286bee4489eaa7ba010586", - "sha256:d97dd44683802000277bbf142fd9f6b271746b4846d0acaf0cefa6b2eaf2a7ad", - "sha256:dcdc88b6b01015da066da3fb76545e8bb9a6880a5ebf89e0f0b2e3ca557b3ab7", - "sha256:dd609fafdcdde6e67a139898196698af37438b035b25ad63704fd9097d9a3482", - "sha256:defa2c0c68734f4a82028c26bcc85e6b92cced99866af118cd6a89b734ad8e0d", - "sha256:e22260a4741a0e7a206e175232867b48a16e0401ef5bce3c67ca5b9705879066", - "sha256:e225a6a14ecf44499aadea165299092ab0cba918bb9ccd9304eab1138844490b", - "sha256:e3df0bc35e746cce42579826b89579d13fd27c3d5319a6afca9893a9b784ff1b", - "sha256:e6fcc026a3f27c1282c7ed24b7fcac82cdd70a0e84cc848c0841a3ab1e3dea2d", - "sha256:e782379c2028a3611285a795b89b99a52722946d19fc06f002f8b53e3ea26ea9", - "sha256:e8cdd52744f680346ff8c1ecdad5f4d11117e1724d4f4e1874f3a67598821069", - "sha256:e9616f5bd2595f7f4a04b67039d890348ab826e943a9bfdbe4938d0eba606971", - "sha256:e98c4c07ee4c4b3acf787e91b27688409d918212dfd34c872201273fdd5a0e18", - "sha256:ebdab79f42c5961682654b851f3f0fc68e6cc7cd8727c2ac4ffff955154123c1", - "sha256:f0f17f2ce0f3529177a5fff5525204fad7b43dd437d017dd0317f2746773443d", - "sha256:f4e56860a5af16a0fcfa070a0a20c42fbb2012eed1eb5ceeddcc7f8079214281" - ], - "markers": "python_version >= '3.8'", - "version": "==0.10.6" - }, - "setuptools": { - "hashes": [ - "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87", - "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a" - ], - "markers": "python_version >= '3.8'", - "version": "==68.2.2" - }, - "sh": { - "hashes": [ - "sha256:9b2998f313f201c777e2c0061f0b1367497097ef13388595be147e2a00bf7ba1", - "sha256:ced8f2e081a858b66a46ace3703dec243779abbd5a1887ba7e3c34f34da70cd2" - ], - "index": "pypi", - "markers": "python_version < '4.0' and python_full_version >= '3.8.1'", - "version": "==2.0.6" - }, - "shapely": { - "hashes": [ - "sha256:03e63a99dfe6bd3beb8d5f41ec2086585bb969991d603f9aeac335ad396a06d4", - "sha256:0521d76d1e8af01e712db71da9096b484f081e539d4f4a8c97342e7971d5e1b4", - "sha256:06f193091a7c6112fc08dfd195a1e3846a64306f890b151fa8c63b3e3624202c", - "sha256:084b023dae8ad3d5b98acee9d3bf098fdf688eb0bb9b1401e8b075f6a627b611", - "sha256:1713cc04c171baffc5b259ba8531c58acc2a301707b7f021d88a15ed090649e7", - "sha256:1f217d28ecb48e593beae20a0082a95bd9898d82d14b8fcb497edf6bff9a44d7", - "sha256:2d217e56ae067e87b4e1731d0dc62eebe887ced729ba5c2d4590e9e3e9fdbd88", - "sha256:34eac2337cbd67650248761b140d2535855d21b969d76d76123317882d3a0c1a", - "sha256:36480e32c434d168cdf2f5e9862c84aaf4d714a43a8465ae3ce8ff327f0affb7", - "sha256:394e5085b49334fd5b94fa89c086edfb39c3ecab7f669e8b2a4298b9d523b3a5", - "sha256:42997ac806e4583dad51c80a32d38570fd9a3d4778f5e2c98f9090aa7db0fe91", - "sha256:45ac6906cff0765455a7b49c1670af6e230c419507c13e2f75db638c8fc6f3bd", - "sha256:4ef753200cbffd4f652efb2c528c5474e5a14341a473994d90ad0606522a46a2", - "sha256:5324be299d4c533ecfcfd43424dfd12f9428fd6f12cda38a4316da001d6ef0ea", - "sha256:5b0c052709c8a257c93b0d4943b0b7a3035f87e2d6a8ac9407b6a992d206422f", - "sha256:6a21353d28209fb0d8cc083e08ca53c52666e0d8a1f9bbe23b6063967d89ed24", - "sha256:6ca8cffbe84ddde8f52b297b53f8e0687bd31141abb2c373fd8a9f032df415d6", - "sha256:72b5997272ae8c25f0fd5b3b967b3237e87fab7978b8d6cd5fa748770f0c5d68", - "sha256:737dba15011e5a9b54a8302f1748b62daa207c9bc06f820cd0ad32a041f1c6f2", - "sha256:78128357a0cee573257a0c2c388d4b7bf13cb7dbe5b3fe5d26d45ebbe2a39e25", - "sha256:794affd80ca0f2c536fc948a3afa90bd8fb61ebe37fe873483ae818e7f21def4", - "sha256:7e92e7c255f89f5cdf777690313311f422aa8ada9a3205b187113274e0135cd8", - "sha256:87dc2be34ac3a3a4a319b963c507ac06682978a5e6c93d71917618b14f13066e", - "sha256:94ac128ae2ab4edd0bffcd4e566411ea7bdc738aeaf92c32a8a836abad725f9f", - "sha256:a5533a925d8e211d07636ffc2fdd9a7f9f13d54686d00577eeb11d16f00be9c4", - "sha256:a9a41ff4323fc9d6257759c26eb1cf3a61ebc7e611e024e6091f42977303fd3a", - "sha256:b8eb0a92f7b8c74f9d8fdd1b40d395113f59bd8132ca1348ebcc1f5aece94b96", - "sha256:baa14fc27771e180c06b499a0a7ba697c7988c7b2b6cba9a929a19a4d2762de3", - "sha256:be46d5509b9251dd9087768eaf35a71360de6afac82ce87c636990a0871aa18b", - "sha256:c6fd29fbd9cd76350bd5cc14c49de394a31770aed02d74203e23b928f3d2f1aa", - "sha256:ccfd5fa10a37e67dbafc601c1ddbcbbfef70d34c3f6b0efc866ddbdb55893a6c", - "sha256:d41a116fcad58048d7143ddb01285e1a8780df6dc1f56c3b1e1b7f12ed296651", - "sha256:dc9342fc82e374130db86a955c3c4525bfbf315a248af8277a913f30911bed9e", - "sha256:dea9a0651333cf96ef5bb2035044e3ad6a54f87d90e50fe4c2636debf1b77abc", - "sha256:e7c95d3379ae3abb74058938a9fcbc478c6b2e28d20dace38f8b5c587dde90aa", - "sha256:e7d897e6bdc6bc64f7f65155dbbb30e49acaabbd0d9266b9b4041f87d6e52b3a", - "sha256:ea84d1cdbcf31e619d672b53c4532f06253894185ee7acb8ceb78f5f33cbe033", - "sha256:ed1e99702125e7baccf401830a3b94d810d5c70b329b765fe93451fe14cf565b", - "sha256:eebe544df5c018134f3c23b6515877f7e4cd72851f88a8d0c18464f414d141a2", - "sha256:fa3ee28f5e63a130ec5af4dc3c4cb9c21c5788bb13c15e89190d163b14f9fb89", - "sha256:fd3ad17b64466a033848c26cb5b509625c87d07dcf39a1541461cacdb8f7e91c" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.2" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "sniffio": { - "hashes": [ - "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101", - "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384" - ], - "markers": "python_version >= '3.7'", - "version": "==1.3.0" - }, - "sortedcontainers": { - "hashes": [ - "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", - "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" - ], - "version": "==2.4.0" - }, - "soupsieve": { - "hashes": [ - "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690", - "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7" - ], - "markers": "python_version >= '3.8'", - "version": "==2.5" - }, - "sqlalchemy": { - "hashes": [ - "sha256:0b7dbe6369677a2bea68fe9812c6e4bbca06ebfa4b5cde257b2b0bf208709131", - "sha256:128a948bd40780667114b0297e2cc6d657b71effa942e0a368d8cc24293febb3", - "sha256:14b0cacdc8a4759a1e1bd47dc3ee3f5db997129eb091330beda1da5a0e9e5bd7", - "sha256:1fb9cb60e0f33040e4f4681e6658a7eb03b5cb4643284172f91410d8c493dace", - "sha256:273505fcad22e58cc67329cefab2e436006fc68e3c5423056ee0513e6523268a", - "sha256:2e70e0673d7d12fa6cd363453a0d22dac0d9978500aa6b46aa96e22690a55eab", - "sha256:34e1c5d9cd3e6bf3d1ce56971c62a40c06bfc02861728f368dcfec8aeedb2814", - "sha256:3b97ddf509fc21e10b09403b5219b06c5b558b27fc2453150274fa4e70707dbf", - "sha256:3f6997da81114daef9203d30aabfa6b218a577fc2bd797c795c9c88c9eb78d49", - "sha256:82dd4131d88395df7c318eeeef367ec768c2a6fe5bd69423f7720c4edb79473c", - "sha256:85292ff52ddf85a39367057c3d7968a12ee1fb84565331a36a8fead346f08796", - "sha256:8a7a66297e46f85a04d68981917c75723e377d2e0599d15fbe7a56abed5e2d75", - "sha256:8b881ac07d15fb3e4f68c5a67aa5cdaf9eb8f09eb5545aaf4b0a5f5f4659be18", - "sha256:a3257a6e09626d32b28a0c5b4f1a97bced585e319cfa90b417f9ab0f6145c33c", - "sha256:a9bddb60566dc45c57fd0a5e14dd2d9e5f106d2241e0a2dc0c1da144f9444516", - "sha256:bdb77e1789e7596b77fd48d99ec1d2108c3349abd20227eea0d48d3f8cf398d9", - "sha256:c1db0221cb26d66294f4ca18c533e427211673ab86c1fbaca8d6d9ff78654293", - "sha256:c4cb501d585aa74a0f86d0ea6263b9c5e1d1463f8f9071392477fd401bd3c7cc", - "sha256:d00665725063692c42badfd521d0c4392e83c6c826795d38eb88fb108e5660e5", - "sha256:d0fed0f791d78e7767c2db28d34068649dfeea027b83ed18c45a423f741425cb", - "sha256:d69738d582e3a24125f0c246ed8d712b03bd21e148268421e4a4d09c34f521a5", - "sha256:db4db3c08ffbb18582f856545f058a7a5e4ab6f17f75795ca90b3c38ee0a8ba4", - "sha256:f1fcee5a2c859eecb4ed179edac5ffbc7c84ab09a5420219078ccc6edda45436", - "sha256:f2d526aeea1bd6a442abc7c9b4b00386fd70253b80d54a0930c0a216230a35be", - "sha256:fbaf6643a604aa17e7a7afd74f665f9db882df5c297bdd86c38368f2c471f37d" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.4.50" - }, - "stack-data": { - "hashes": [ - "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", - "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695" - ], - "version": "==0.6.3" - }, - "tabulate": { - "hashes": [ - "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", - "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f" - ], - "markers": "python_version >= '3.7'", - "version": "==0.9.0" - }, - "tblib": { - "hashes": [ - "sha256:80a6c77e59b55e83911e1e607c649836a69c103963c5f28a46cbeef44acf8129", - "sha256:93622790a0a29e04f0346458face1e144dc4d32f493714c6c3dff82a4adb77e6" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, - "tenacity": { - "hashes": [ - "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a", - "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c" - ], - "markers": "python_version >= '3.7'", - "version": "==8.2.3" - }, - "text-unidecode": { - "hashes": [ - "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", - "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93" - ], - "version": "==1.3" - }, - "textwrap3": { - "hashes": [ - "sha256:5008eeebdb236f6303dcd68f18b856d355f6197511d952ba74bc75e40e0c3414", - "sha256:bf5f4c40faf2a9ff00a9e0791fed5da7415481054cef45bb4a3cfb1f69044ae0" - ], - "version": "==0.9.2" - }, - "tinycss2": { - "hashes": [ - "sha256:2b80a96d41e7c3914b8cda8bc7f705a4d9c49275616e886103dd839dfc847847", - "sha256:8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627" - ], - "markers": "python_version >= '3.7'", - "version": "==1.2.1" - }, - "tokenize-rt": { - "hashes": [ - "sha256:9fe80f8a5c1edad2d3ede0f37481cc0cc1538a2f442c9c2f9e4feacd2792d054", - "sha256:b79d41a65cfec71285433511b50271b05da3584a1da144a0752e9c621a285289" - ], - "version": "==5.2.0" - }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.2" - }, - "tomli": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_version < '3.11'", - "version": "==2.0.1" - }, - "toolz": { - "hashes": [ - "sha256:2059bd4148deb1884bb0eb770a3cde70e7f954cfbbdc2285f1f2de01fd21eb6f", - "sha256:88c570861c440ee3f2f6037c4654613228ff40c93a6c25e0eba70d17282c6194" - ], - "markers": "python_version >= '3.5'", - "version": "==0.12.0" - }, - "tornado": { - "hashes": [ - "sha256:1bd19ca6c16882e4d37368e0152f99c099bad93e0950ce55e71daed74045908f", - "sha256:22d3c2fa10b5793da13c807e6fc38ff49a4f6e1e3868b0a6f4164768bb8e20f5", - "sha256:502fba735c84450974fec147340016ad928d29f1e91f49be168c0a4c18181e1d", - "sha256:65ceca9500383fbdf33a98c0087cb975b2ef3bfb874cb35b8de8740cf7f41bd3", - "sha256:71a8db65160a3c55d61839b7302a9a400074c9c753040455494e2af74e2501f2", - "sha256:7ac51f42808cca9b3613f51ffe2a965c8525cb1b00b7b2d56828b8045354f76a", - "sha256:7d01abc57ea0dbb51ddfed477dfe22719d376119844e33c661d873bf9c0e4a16", - "sha256:805d507b1f588320c26f7f097108eb4023bbaa984d63176d1652e184ba24270a", - "sha256:9dc4444c0defcd3929d5c1eb5706cbe1b116e762ff3e0deca8b715d14bf6ec17", - "sha256:ceb917a50cd35882b57600709dd5421a418c29ddc852da8bcdab1f0db33406b0", - "sha256:e7d8db41c0181c80d76c982aacc442c0783a2c54d6400fe028954201a2e032fe" - ], - "markers": "python_version >= '3.8'", - "version": "==6.3.3" - }, - "tqdm": { - "hashes": [ - "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386", - "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7" - ], - "markers": "python_version >= '3.7'", - "version": "==4.66.1" - }, - "traitlets": { - "hashes": [ - "sha256:9b232b9430c8f57288c1024b34a8f0251ddcc47268927367a0dd3eeaca40deb5", - "sha256:baf991e61542da48fe8aef8b779a9ea0aa38d8a54166ee250d5af5ecf4486619" - ], - "markers": "python_version >= '3.8'", - "version": "==5.13.0" - }, - "typing-extensions": { - "hashes": [ - "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", - "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" - ], - "markers": "python_version < '3.10'", - "version": "==4.8.0" - }, - "tzdata": { - "hashes": [ - "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a", - "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda" - ], - "markers": "python_version >= '2'", - "version": "==2023.3" - }, - "ujson": { - "hashes": [ - "sha256:07d459aca895eb17eb463b00441986b021b9312c6c8cc1d06880925c7f51009c", - "sha256:0be81bae295f65a6896b0c9030b55a106fb2dec69ef877253a87bc7c9c5308f7", - "sha256:0fe1b7edaf560ca6ab023f81cbeaf9946a240876a993b8c5a21a1c539171d903", - "sha256:102bf31c56f59538cccdfec45649780ae00657e86247c07edac434cb14d5388c", - "sha256:11da6bed916f9bfacf13f4fc6a9594abd62b2bb115acfb17a77b0f03bee4cfd5", - "sha256:16fde596d5e45bdf0d7de615346a102510ac8c405098e5595625015b0d4b5296", - "sha256:193349a998cd821483a25f5df30b44e8f495423840ee11b3b28df092ddfd0f7f", - "sha256:20768961a6a706170497129960762ded9c89fb1c10db2989c56956b162e2a8a3", - "sha256:27a2a3c7620ebe43641e926a1062bc04e92dbe90d3501687957d71b4bdddaec4", - "sha256:2873d196725a8193f56dde527b322c4bc79ed97cd60f1d087826ac3290cf9207", - "sha256:299a312c3e85edee1178cb6453645217ba23b4e3186412677fa48e9a7f986de6", - "sha256:2a64cc32bb4a436e5813b83f5aab0889927e5ea1788bf99b930fad853c5625cb", - "sha256:2b852bdf920fe9f84e2a2c210cc45f1b64f763b4f7d01468b33f7791698e455e", - "sha256:2e72ba76313d48a1a3a42e7dc9d1db32ea93fac782ad8dde6f8b13e35c229130", - "sha256:3659deec9ab9eb19e8646932bfe6fe22730757c4addbe9d7d5544e879dc1b721", - "sha256:3b27a8da7a080add559a3b73ec9ebd52e82cc4419f7c6fb7266e62439a055ed0", - "sha256:3f9b63530a5392eb687baff3989d0fb5f45194ae5b1ca8276282fb647f8dcdb3", - "sha256:407d60eb942c318482bbfb1e66be093308bb11617d41c613e33b4ce5be789adc", - "sha256:40931d7c08c4ce99adc4b409ddb1bbb01635a950e81239c2382cfe24251b127a", - "sha256:48c7d373ff22366eecfa36a52b9b55b0ee5bd44c2b50e16084aa88b9de038916", - "sha256:4ddeabbc78b2aed531f167d1e70387b151900bc856d61e9325fcdfefb2a51ad8", - "sha256:5ac97b1e182d81cf395ded620528c59f4177eee024b4b39a50cdd7b720fdeec6", - "sha256:5ce24909a9c25062e60653073dd6d5e6ec9d6ad7ed6e0069450d5b673c854405", - "sha256:69b3104a2603bab510497ceabc186ba40fef38ec731c0ccaa662e01ff94a985c", - "sha256:6a4dafa9010c366589f55afb0fd67084acd8added1a51251008f9ff2c3e44042", - "sha256:6d230d870d1ce03df915e694dcfa3f4e8714369cce2346686dbe0bc8e3f135e7", - "sha256:78e318def4ade898a461b3d92a79f9441e7e0e4d2ad5419abed4336d702c7425", - "sha256:7a42baa647a50fa8bed53d4e242be61023bd37b93577f27f90ffe521ac9dc7a3", - "sha256:7cba16b26efe774c096a5e822e4f27097b7c81ed6fb5264a2b3f5fd8784bab30", - "sha256:7d8283ac5d03e65f488530c43d6610134309085b71db4f675e9cf5dff96a8282", - "sha256:7ecc33b107ae88405aebdb8d82c13d6944be2331ebb04399134c03171509371a", - "sha256:9249fdefeb021e00b46025e77feed89cd91ffe9b3a49415239103fc1d5d9c29a", - "sha256:9399eaa5d1931a0ead49dce3ffacbea63f3177978588b956036bfe53cdf6af75", - "sha256:94c7bd9880fa33fcf7f6d7f4cc032e2371adee3c5dba2922b918987141d1bf07", - "sha256:9571de0c53db5cbc265945e08f093f093af2c5a11e14772c72d8e37fceeedd08", - "sha256:9721cd112b5e4687cb4ade12a7b8af8b048d4991227ae8066d9c4b3a6642a582", - "sha256:9ab282d67ef3097105552bf151438b551cc4bedb3f24d80fada830f2e132aeb9", - "sha256:9d9707e5aacf63fb919f6237d6490c4e0244c7f8d3dc2a0f84d7dec5db7cb54c", - "sha256:a70f776bda2e5072a086c02792c7863ba5833d565189e09fabbd04c8b4c3abba", - "sha256:a89cf3cd8bf33a37600431b7024a7ccf499db25f9f0b332947fbc79043aad879", - "sha256:a8c91b6f4bf23f274af9002b128d133b735141e867109487d17e344d38b87d94", - "sha256:ad24ec130855d4430a682c7a60ca0bc158f8253ec81feed4073801f6b6cb681b", - "sha256:ae7f4725c344bf437e9b881019c558416fe84ad9c6b67426416c131ad577df67", - "sha256:b748797131ac7b29826d1524db1cc366d2722ab7afacc2ce1287cdafccddbf1f", - "sha256:bdf04c6af3852161be9613e458a1fb67327910391de8ffedb8332e60800147a2", - "sha256:bf5737dbcfe0fa0ac8fa599eceafae86b376492c8f1e4b84e3adf765f03fb564", - "sha256:c4e7bb7eba0e1963f8b768f9c458ecb193e5bf6977090182e2b4f4408f35ac76", - "sha256:d524a8c15cfc863705991d70bbec998456a42c405c291d0f84a74ad7f35c5109", - "sha256:d53039d39de65360e924b511c7ca1a67b0975c34c015dd468fca492b11caa8f7", - "sha256:d6f84a7a175c75beecde53a624881ff618e9433045a69fcfb5e154b73cdaa377", - "sha256:e0147d41e9fb5cd174207c4a2895c5e24813204499fd0839951d4c8784a23bf5", - "sha256:e3673053b036fd161ae7a5a33358ccae6793ee89fd499000204676baafd7b3aa", - "sha256:e54578fa8838ddc722539a752adfce9372474114f8c127bb316db5392d942f8b", - "sha256:eb0142f6f10f57598655340a3b2c70ed4646cbe674191da195eb0985a9813b83", - "sha256:efeddf950fb15a832376c0c01d8d7713479fbeceaed1eaecb2665aa62c305aec", - "sha256:f26629ac531d712f93192c233a74888bc8b8212558bd7d04c349125f10199fcf", - "sha256:f2e385a7679b9088d7bc43a64811a7713cc7c33d032d020f757c54e7d41931ae", - "sha256:f3554eaadffe416c6f543af442066afa6549edbc34fe6a7719818c3e72ebfe95", - "sha256:f4511560d75b15ecb367eef561554959b9d49b6ec3b8d5634212f9fed74a6df1", - "sha256:f504117a39cb98abba4153bf0b46b4954cc5d62f6351a14660201500ba31fe7f", - "sha256:fb87decf38cc82bcdea1d7511e73629e651bdec3a43ab40985167ab8449b769c" - ], - "markers": "python_version >= '3.8'", - "version": "==5.8.0" - }, - "urllib3": { - "hashes": [ - "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", - "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.7" - }, - "wcwidth": { - "hashes": [ - "sha256:9a929bd8380f6cd9571a968a9c8f4353ca58d7cd812a4822bba831f8d685b223", - "sha256:a675d1a4a2d24ef67096a04b85b02deeecd8e226f57b5e3a72dbb9ed99d27da8" - ], - "version": "==0.2.9" - }, - "webencodings": { - "hashes": [ - "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", - "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" - ], - "version": "==0.5.1" - }, - "websocket-client": { - "hashes": [ - "sha256:084072e0a7f5f347ef2ac3d8698a5e0b4ffbfcab607628cadabc650fc9a83a24", - "sha256:b3324019b3c28572086c4a319f91d1dcd44e6e11cd340232978c684a7650d0df" - ], - "markers": "python_version >= '3.8'", - "version": "==1.6.4" - }, - "widgetsnbextension": { - "hashes": [ - "sha256:3c1f5e46dc1166dfd40a42d685e6a51396fd34ff878742a3e47c6f0cc4a2a385", - "sha256:91452ca8445beb805792f206e560c1769284267a30ceb1cec9f5bcc887d15175" - ], - "markers": "python_version >= '3.7'", - "version": "==4.0.9" - }, - "zict": { - "hashes": [ - "sha256:5796e36bd0e0cc8cf0fbc1ace6a68912611c1dbd74750a3f3026b9b9d6a327ae", - "sha256:e321e263b6a97aafc0790c3cfb3c04656b7066e6738c37fffcca95d803c9fba5" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, - "zipp": { - "hashes": [ - "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31", - "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0" - ], - "markers": "python_version >= '3.8'", - "version": "==3.17.0" - } - }, - "develop": { - "anyio": { - "hashes": [ - "sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f", - "sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a" - ], - "markers": "python_version >= '3.8'", - "version": "==4.0.0" - }, - "appnope": { - "hashes": [ - "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24", - "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e" - ], - "markers": "platform_system == 'Darwin'", - "version": "==0.1.3" - }, - "argon2-cffi": { - "hashes": [ - "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08", - "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1.0" - }, - "argon2-cffi-bindings": { - "hashes": [ - "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670", - "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f", - "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583", - "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194", - "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c", - "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a", - "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082", - "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5", - "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f", - "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7", - "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d", - "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f", - "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae", - "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3", - "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86", - "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367", - "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d", - "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93", - "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb", - "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e", - "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351" - ], - "markers": "python_version >= '3.6'", - "version": "==21.2.0" - }, - "arrow": { - "hashes": [ - "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80", - "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85" - ], - "markers": "python_version >= '3.8'", - "version": "==1.3.0" - }, - "asn1crypto": { - "hashes": [ - "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c", - "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67" - ], - "version": "==1.5.1" - }, - "asttokens": { - "hashes": [ - "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", - "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0" - ], - "version": "==2.4.1" - }, - "async-lru": { - "hashes": [ - "sha256:b8a59a5df60805ff63220b2a0c5b5393da5521b113cd5465a44eb037d81a5627", - "sha256:ff02944ce3c288c5be660c42dbcca0742b32c3b279d6dceda655190240b99224" - ], - "markers": "python_version >= '3.8'", - "version": "==2.0.4" - }, - "attrs": { - "hashes": [ - "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1.0" - }, - "babel": { - "hashes": [ - "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900", - "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed" - ], - "markers": "python_version >= '3.7'", - "version": "==2.13.1" - }, - "backcall": { - "hashes": [ - "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e", - "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255" - ], - "version": "==0.2.0" - }, - "beautifulsoup4": { - "hashes": [ - "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da", - "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a" - ], - "markers": "python_full_version >= '3.6.0'", - "version": "==4.12.2" - }, - "bleach": { - "hashes": [ - "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe", - "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6" - ], - "markers": "python_version >= '3.8'", - "version": "==6.1.0" - }, - "certifi": { - "hashes": [ - "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", - "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" - ], - "markers": "python_version >= '3.6'", - "version": "==2023.7.22" - }, - "cffi": { - "hashes": [ - "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", - "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", - "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", - "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", - "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", - "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", - "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", - "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", - "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", - "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", - "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", - "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", - "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", - "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", - "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", - "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", - "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", - "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", - "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", - "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", - "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", - "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", - "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", - "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", - "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", - "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", - "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", - "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", - "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", - "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", - "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", - "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", - "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", - "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", - "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", - "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", - "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", - "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", - "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", - "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", - "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", - "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", - "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", - "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", - "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", - "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", - "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", - "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", - "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", - "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", - "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", - "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" - ], - "markers": "python_version >= '3.8'", - "version": "==1.16.0" - }, - "charset-normalizer": { - "hashes": [ - "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5", - "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93", - "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a", - "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d", - "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c", - "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1", - "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58", - "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2", - "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557", - "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147", - "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041", - "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2", - "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2", - "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7", - "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296", - "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690", - "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67", - "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57", - "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597", - "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846", - "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b", - "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97", - "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c", - "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62", - "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa", - "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f", - "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e", - "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821", - "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3", - "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4", - "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb", - "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727", - "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514", - "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d", - "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761", - "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55", - "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f", - "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c", - "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034", - "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6", - "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae", - "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1", - "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14", - "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1", - "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228", - "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708", - "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48", - "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f", - "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5", - "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f", - "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4", - "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8", - "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff", - "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61", - "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b", - "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97", - "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b", - "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605", - "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728", - "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d", - "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c", - "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf", - "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673", - "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1", - "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b", - "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41", - "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8", - "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f", - "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4", - "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008", - "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9", - "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5", - "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f", - "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e", - "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273", - "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45", - "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e", - "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656", - "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e", - "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c", - "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2", - "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72", - "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056", - "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397", - "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42", - "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd", - "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3", - "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213", - "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf", - "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.1" - }, - "click": { - "hashes": [ - "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", - "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.7" - }, - "cloudpickle": { - "hashes": [ - "sha256:246ee7d0c295602a036e86369c77fecda4ab17b506496730f2f576d9016fd9c7", - "sha256:996d9a482c6fb4f33c1a35335cf8afd065d2a56e973270364840712d9131a882" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, - "comm": { - "hashes": [ - "sha256:354e40a59c9dd6db50c5cc6b4acc887d82e9603787f83b68c01a80a923984d15", - "sha256:6d52794cba11b36ed9860999cd10fd02d6b2eac177068fdd585e1e2f8a96e67a" - ], - "markers": "python_version >= '3.6'", - "version": "==0.1.4" - }, - "coverage": { - "extras": [ - "toml" - ], - "hashes": [ - "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1", - "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63", - "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9", - "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312", - "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3", - "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb", - "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25", - "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92", - "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda", - "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148", - "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6", - "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216", - "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a", - "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640", - "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836", - "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c", - "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f", - "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2", - "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901", - "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed", - "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a", - "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074", - "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc", - "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84", - "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083", - "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f", - "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c", - "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c", - "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637", - "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2", - "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82", - "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f", - "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce", - "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef", - "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f", - "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611", - "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c", - "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76", - "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9", - "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce", - "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9", - "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf", - "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf", - "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9", - "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6", - "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2", - "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a", - "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a", - "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf", - "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738", - "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a", - "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4" - ], - "markers": "python_version >= '3.8'", - "version": "==7.3.2" - }, - "croniter": { - "hashes": [ - "sha256:4cb064ce2d8f695b3b078be36ff50115cf8ac306c10a7e8653ee2a5b534673d7", - "sha256:d199b2ec3ea5e82988d1f72022433c5f9302b3b3ea9e6bfd6a1518f6ea5e700a" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.0.1" - }, - "dask": { - "hashes": [ - "sha256:32b34986519b7ddc0947c8ca63c2fc81b964e4c208dfb5cbf9f4f8aec92d152b", - "sha256:4f4c28ac406e81b8f21b5be4b31b21308808f3e0e7c7e2f4a914f16476d9941b" - ], - "markers": "python_version >= '3.8'", - "version": "==2023.5.0" - }, - "debugpy": { - "hashes": [ - "sha256:125b9a637e013f9faac0a3d6a82bd17c8b5d2c875fb6b7e2772c5aba6d082332", - "sha256:12af2c55b419521e33d5fb21bd022df0b5eb267c3e178f1d374a63a2a6bdccd0", - "sha256:3c6fb41c98ec51dd010d7ed650accfd07a87fe5e93eca9d5f584d0578f28f35f", - "sha256:46ab6780159eeabb43c1495d9c84cf85d62975e48b6ec21ee10c95767c0590aa", - "sha256:57161629133113c97b387382045649a2b985a348f0c9366e22217c87b68b73c6", - "sha256:5d9de202f5d42e62f932507ee8b21e30d49aae7e46d5b1dd5c908db1d7068637", - "sha256:60009b132c91951354f54363f8ebdf7457aeb150e84abba5ae251b8e9f29a8a6", - "sha256:61eab4a4c8b6125d41a34bad4e5fe3d2cc145caecd63c3fe953be4cc53e65bf8", - "sha256:7fb95ca78f7ac43393cd0e0f2b6deda438ec7c5e47fa5d38553340897d2fbdfb", - "sha256:8cd0197141eb9e8a4566794550cfdcdb8b3db0818bdf8c49a8e8f8053e56e38b", - "sha256:9c9b0ac1ce2a42888199df1a1906e45e6f3c9555497643a85e0bf2406e3ffbc4", - "sha256:a64093656c4c64dc6a438e11d59369875d200bd5abb8f9b26c1f5f723622e153", - "sha256:a8b7a2fd27cd9f3553ac112f356ad4ca93338feadd8910277aff71ab24d8775f", - "sha256:b05a6b503ed520ad58c8dc682749113d2fd9f41ffd45daec16e558ca884008cd", - "sha256:bdc5ef99d14b9c0fcb35351b4fbfc06ac0ee576aeab6b2511702e5a648a2e595", - "sha256:e3412f9faa9ade82aa64a50b602544efcba848c91384e9f93497a458767e6926", - "sha256:ef54404365fae8d45cf450d0544ee40cefbcb9cb85ea7afe89a963c27028261e", - "sha256:ef9ab7df0b9a42ed9c878afd3eaaff471fce3fa73df96022e1f5c9f8f8c87ada" - ], - "markers": "python_version >= '3.8'", - "version": "==1.8.0" - }, - "decorator": { - "hashes": [ - "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", - "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186" - ], - "markers": "python_version >= '3.5'", - "version": "==5.1.1" - }, - "defusedxml": { - "hashes": [ - "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", - "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.7.1" - }, - "distributed": { - "hashes": [ - "sha256:73ce33bd2460bd45ffc793ffdf9066bd2a3b6bbc65079f74f5147eafcda9b1cb", - "sha256:74e3f7f68d4dc435a3591ae1ad8ce7d5a11211fd22692e39c7e50aa11bf7e385" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==2023.5.0" - }, - "docker": { - "hashes": [ - "sha256:aa6d17830045ba5ef0168d5eaa34d37beeb113948c413affe1d5991fc11f9a20", - "sha256:aecd2277b8bf8e506e484f6ab7aec39abe0038e29fa4a6d3ba86c3fe01844ed9" - ], - "markers": "python_version >= '3.7'", - "version": "==6.1.3" - }, - "exceptiongroup": { - "hashes": [ - "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9", - "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3" - ], - "markers": "python_version < '3.11'", - "version": "==1.1.3" - }, - "executing": { - "hashes": [ - "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147", - "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc" - ], - "markers": "python_version >= '3.5'", - "version": "==2.0.1" - }, - "fastjsonschema": { - "hashes": [ - "sha256:06dc8680d937628e993fa0cd278f196d20449a1adc087640710846b324d422ea", - "sha256:aec6a19e9f66e9810ab371cc913ad5f4e9e479b63a7072a2cd060a9369e329a8" - ], - "version": "==2.18.1" - }, - "fqdn": { - "hashes": [ - "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", - "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014" - ], - "version": "==1.5.1" - }, - "fsspec": { - "hashes": [ - "sha256:330c66757591df346ad3091a53bd907e15348c2ba17d63fd54f5c39c4457d2a5", - "sha256:346a8f024efeb749d2a5fca7ba8854474b1ff9af7c3faaf636a4548781136529" - ], - "markers": "python_version >= '3.8'", - "version": "==2023.10.0" - }, - "graphviz": { - "hashes": [ - "sha256:587c58a223b51611c0cf461132da386edd896a029524ca61a1462b880bf97977", - "sha256:8c58f14adaa3b947daf26c19bc1e98c4e0702cdc31cf99153e6f06904d492bf8" - ], - "version": "==0.20.1" - }, - "greenlet": { - "hashes": [ - "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174", - "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd", - "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa", - "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a", - "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec", - "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565", - "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d", - "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c", - "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234", - "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d", - "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546", - "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2", - "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74", - "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de", - "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd", - "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9", - "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3", - "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846", - "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2", - "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353", - "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8", - "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166", - "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206", - "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b", - "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d", - "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe", - "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997", - "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445", - "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0", - "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96", - "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884", - "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6", - "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1", - "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619", - "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94", - "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4", - "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1", - "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63", - "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd", - "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a", - "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376", - "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57", - "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16", - "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e", - "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc", - "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a", - "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c", - "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5", - "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a", - "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72", - "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9", - "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9", - "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e", - "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8", - "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65", - "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064", - "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36" - ], - "markers": "python_version >= '3.7'", - "version": "==3.0.1" - }, - "idna": { - "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" - ], - "markers": "python_version >= '3.5'", - "version": "==3.4" - }, - "importlib-metadata": { - "hashes": [ - "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb", - "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743" - ], - "markers": "python_version < '3.10'", - "version": "==6.8.0" - }, - "importlib-resources": { - "hashes": [ - "sha256:9d48dcccc213325e810fd723e7fbb45ccb39f6cf5c31f00cf2b965f5f10f3cb9", - "sha256:aa50258bbfa56d4e33fbd8aa3ef48ded10d1735f11532b8df95388cc6bdb7e83" - ], - "markers": "python_version < '3.10'", - "version": "==6.1.0" - }, - "iniconfig": { - "hashes": [ - "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", - "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.0" - }, - "ipykernel": { - "hashes": [ - "sha256:3ba3dc97424b87b31bb46586b5167b3161b32d7820b9201a9e698c71e271602c", - "sha256:553856658eb8430bbe9653ea041a41bff63e9606fc4628873fc92a6cf3abd404" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==6.26.0" - }, - "ipython": { - "hashes": [ - "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363", - "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c" - ], - "markers": "python_version >= '3.3'", - "version": "==8.12.3" - }, - "ipywidgets": { - "hashes": [ - "sha256:2b88d728656aea3bbfd05d32c747cfd0078f9d7e159cf982433b58ad717eed7f", - "sha256:40211efb556adec6fa450ccc2a77d59ca44a060f4f9f136833df59c9f538e6e8" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.1" - }, - "isoduration": { - "hashes": [ - "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", - "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042" - ], - "version": "==20.11.0" - }, - "jedi": { - "hashes": [ - "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd", - "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0" - ], - "markers": "python_version >= '3.6'", - "version": "==0.19.1" - }, - "jinja2": { - "hashes": [ - "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", - "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.2" - }, - "json5": { - "hashes": [ - "sha256:740c7f1b9e584a468dbb2939d8d458db3427f2c93ae2139d05f47e453eae964f", - "sha256:9ed66c3a6ca3510a976a9ef9b8c0787de24802724ab1860bc0153c7fdd589b02" - ], - "version": "==0.9.14" - }, - "jsonpointer": { - "hashes": [ - "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a", - "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88" - ], - "version": "==2.4" - }, - "jsonschema": { - "hashes": [ - "sha256:c9ff4d7447eed9592c23a12ccee508baf0dd0d59650615e847feb6cdca74f392", - "sha256:eee9e502c788e89cb166d4d37f43084e3b64ab405c795c03d343a4dbc2c810fc" - ], - "markers": "python_version >= '3.8'", - "version": "==4.19.2" - }, - "jsonschema-specifications": { - "hashes": [ - "sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1", - "sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb" - ], - "markers": "python_version >= '3.8'", - "version": "==2023.7.1" - }, - "jupyter-client": { - "hashes": [ - "sha256:c3877aac7257ec68d79b5c622ce986bd2a992ca42f6ddc9b4dd1da50e89f7028", - "sha256:e8754066510ce456358df363f97eae64b50860f30dc1fe8c6771440db3be9a63" - ], - "markers": "python_version >= '3.8'", - "version": "==8.5.0" - }, - "jupyter-core": { - "hashes": [ - "sha256:880b86053bf298a8724994f95e99b99130659022a4f7f45f563084b6223861d3", - "sha256:e11e02cd8ae0a9de5c6c44abf5727df9f2581055afe00b22183f621ba3585805" - ], - "markers": "python_version >= '3.8'", - "version": "==5.5.0" - }, - "jupyter-events": { - "hashes": [ - "sha256:81f07375c7673ff298bfb9302b4a981864ec64edaed75ca0fe6f850b9b045525", - "sha256:fda08f0defce5e16930542ce60634ba48e010830d50073c3dfd235759cee77bf" - ], - "markers": "python_version >= '3.8'", - "version": "==0.8.0" - }, - "jupyter-lsp": { - "hashes": [ - "sha256:8ebbcb533adb41e5d635eb8fe82956b0aafbf0fd443b6c4bfa906edeeb8635a1", - "sha256:9e06b8b4f7dd50300b70dd1a78c0c3b0c3d8fa68e0f2d8a5d1fbab62072aca3f" - ], - "markers": "python_version >= '3.8'", - "version": "==2.2.0" - }, - "jupyter-server": { - "hashes": [ - "sha256:21ad1a3d455d5a79ce4bef5201925cd17510c17898cf9d54e3ccfb6b12734948", - "sha256:9ba71be4b9c16e479e4c50c929f8ac4b1015baf90237a08681397a98c76c7e5e" - ], - "markers": "python_version >= '3.8'", - "version": "==2.9.1" - }, - "jupyter-server-terminals": { - "hashes": [ - "sha256:57ab779797c25a7ba68e97bcfb5d7740f2b5e8a83b5e8102b10438041a7eac5d", - "sha256:75779164661cec02a8758a5311e18bb8eb70c4e86c6b699403100f1585a12a36" - ], - "markers": "python_version >= '3.8'", - "version": "==0.4.4" - }, - "jupyterlab": { - "hashes": [ - "sha256:08683045117cc495531fdb39c22ababb9aaac6977a45e67cfad20046564c9c7c", - "sha256:48792efd9f962b2bcda1f87d72168ff122c288b1d97d32109e4a11b33dc862be" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==4.0.7" - }, - "jupyterlab-pygments": { - "hashes": [ - "sha256:2405800db07c9f770863bcf8049a529c3dd4d3e28536638bd7c1c01d2748309f", - "sha256:7405d7fde60819d905a9fa8ce89e4cd830e318cdad22a0030f7a901da705585d" - ], - "markers": "python_version >= '3.7'", - "version": "==0.2.2" - }, - "jupyterlab-server": { - "hashes": [ - "sha256:77c2f1f282d610f95e496e20d5bf1d2a7706826dfb7b18f3378ae2870d272fb7", - "sha256:c9f67a98b295c5dee87f41551b0558374e45d449f3edca153dd722140630dcb2" - ], - "markers": "python_version >= '3.8'", - "version": "==2.25.0" - }, - "jupyterlab-widgets": { - "hashes": [ - "sha256:3cf5bdf5b897bf3bccf1c11873aa4afd776d7430200f765e0686bd352487b58d", - "sha256:6005a4e974c7beee84060fdfba341a3218495046de8ae3ec64888e5fe19fdb4c" - ], - "markers": "python_version >= '3.7'", - "version": "==3.0.9" - }, - "locket": { - "hashes": [ - "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632", - "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.0.0" - }, - "markupsafe": { - "hashes": [ - "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", - "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", - "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", - "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", - "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", - "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", - "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", - "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", - "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", - "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", - "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", - "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", - "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", - "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", - "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", - "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", - "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", - "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", - "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", - "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", - "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", - "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", - "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", - "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", - "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", - "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", - "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", - "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", - "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", - "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", - "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", - "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", - "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", - "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", - "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", - "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", - "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", - "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", - "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", - "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", - "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", - "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", - "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", - "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", - "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", - "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", - "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", - "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", - "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", - "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", - "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", - "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", - "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", - "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", - "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", - "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", - "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", - "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", - "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", - "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.3" - }, - "marshmallow": { - "hashes": [ - "sha256:5d2371bbe42000f2b3fb5eaa065224df7d8f8597bc19a1bbfa5bfe7fba8da889", - "sha256:684939db93e80ad3561392f47be0230743131560a41c5110684c16e21ade0a5c" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==3.20.1" - }, - "marshmallow-oneofschema": { - "hashes": [ - "sha256:62cd2099b29188c92493c2940ee79d1bf2f2619a71721664e5a98ec2faa58237", - "sha256:bd29410a9f2f7457a2b428286e2a80ef76b8ddc3701527dc1f935a88914b02f2" - ], - "markers": "python_version >= '3.6'", - "version": "==3.0.1" - }, - "matplotlib-inline": { - "hashes": [ - "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311", - "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304" - ], - "markers": "python_version >= '3.5'", - "version": "==0.1.6" - }, - "mistune": { - "hashes": [ - "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205", - "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8" - ], - "markers": "python_version >= '3.7'", - "version": "==3.0.2" - }, - "msgpack": { - "hashes": [ - "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862", - "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d", - "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3", - "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672", - "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0", - "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9", - "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee", - "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46", - "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524", - "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819", - "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc", - "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc", - "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1", - "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82", - "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81", - "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6", - "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d", - "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2", - "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c", - "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87", - "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84", - "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e", - "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95", - "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f", - "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b", - "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93", - "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf", - "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61", - "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c", - "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8", - "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d", - "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c", - "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4", - "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba", - "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415", - "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee", - "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d", - "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9", - "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075", - "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f", - "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7", - "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681", - "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329", - "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1", - "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf", - "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c", - "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5", - "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b", - "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5", - "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e", - "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b", - "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad", - "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd", - "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7", - "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002", - "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc" - ], - "markers": "python_version >= '3.8'", - "version": "==1.0.7" - }, - "mypy-extensions": { - "hashes": [ - "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", - "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" - ], - "markers": "python_version >= '3.5'", - "version": "==1.0.0" - }, - "nbclient": { - "hashes": [ - "sha256:25e861299e5303a0477568557c4045eccc7a34c17fc08e7959558707b9ebe548", - "sha256:f9b179cd4b2d7bca965f900a2ebf0db4a12ebff2f36a711cb66861e4ae158e55" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==0.8.0" - }, - "nbconvert": { - "hashes": [ - "sha256:4bedff08848626be544de193b7594d98a048073f392178008ff4f171f5e21d26", - "sha256:8cf1d95e569730f136feb85e4bba25bdcf3a63fefb122d854ddff6771c0ac933" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==7.10.0" - }, - "nbformat": { - "hashes": [ - "sha256:1c5172d786a41b82bcfd0c23f9e6b6f072e8fb49c39250219e4acfff1efe89e9", - "sha256:5f98b5ba1997dff175e77e0c17d5c10a96eaed2cbd1de3533d1fc35d5e111192" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==5.9.2" - }, - "nest-asyncio": { - "hashes": [ - "sha256:25aa2ca0d2a5b5531956b9e273b45cf664cae2b145101d73b86b199978d48fdb", - "sha256:accda7a339a70599cb08f9dd09a67e0c2ef8d8d6f4c07f96ab203f2ae254e48d" - ], - "markers": "python_version >= '3.5'", - "version": "==1.5.8" - }, - "notebook-shim": { - "hashes": [ - "sha256:a83496a43341c1674b093bfcebf0fe8e74cbe7eda5fd2bbc56f8e39e1486c0c7", - "sha256:f69388ac283ae008cd506dda10d0288b09a017d822d5e8c7129a152cbd3ce7e9" - ], - "markers": "python_version >= '3.7'", - "version": "==0.2.3" - }, - "overrides": { - "hashes": [ - "sha256:3ad24583f86d6d7a49049695efe9933e67ba62f0c7625d53c59fa832ce4b8b7d", - "sha256:9502a3cca51f4fac40b5feca985b6703a5c1f6ad815588a7ca9e285b9dca6757" - ], - "markers": "python_version >= '3.6'", - "version": "==7.4.0" - }, - "packaging": { - "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" - ], - "markers": "python_version >= '3.7'", - "version": "==23.2" - }, - "pandocfilters": { - "hashes": [ - "sha256:0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38", - "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.5.0" - }, - "parso": { - "hashes": [ - "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0", - "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75" - ], - "markers": "python_version >= '3.6'", - "version": "==0.8.3" - }, - "partd": { - "hashes": [ - "sha256:27e766663d36c161e2827aa3e28541c992f0b9527d3cca047e13fb3acdb989e6", - "sha256:56c25dd49e6fea5727e731203c466c6e092f308d8f0024e199d02f6aa2167f67" - ], - "markers": "python_version >= '3.7'", - "version": "==1.4.1" - }, - "pendulum": { - "hashes": [ - "sha256:0731f0c661a3cb779d398803655494893c9f581f6488048b3fb629c2342b5394", - "sha256:1245cd0075a3c6d889f581f6325dd8404aca5884dea7223a5566c38aab94642b", - "sha256:29c40a6f2942376185728c9a0347d7c0f07905638c83007e1d262781f1e6953a", - "sha256:2d1619a721df661e506eff8db8614016f0720ac171fe80dda1333ee44e684087", - "sha256:318f72f62e8e23cd6660dbafe1e346950281a9aed144b5c596b2ddabc1d19739", - "sha256:33fb61601083f3eb1d15edeb45274f73c63b3c44a8524703dc143f4212bf3269", - "sha256:3481fad1dc3f6f6738bd575a951d3c15d4b4ce7c82dce37cf8ac1483fde6e8b0", - "sha256:4c9c689747f39d0d02a9f94fcee737b34a5773803a64a5fdb046ee9cac7442c5", - "sha256:7c5ec650cb4bec4c63a89a0242cc8c3cebcec92fcfe937c417ba18277d8560be", - "sha256:94b1fc947bfe38579b28e1cccb36f7e28a15e841f30384b5ad6c5e31055c85d7", - "sha256:9702069c694306297ed362ce7e3c1ef8404ac8ede39f9b28b7c1a7ad8c3959e3", - "sha256:b06a0ca1bfe41c990bbf0c029f0b6501a7f2ec4e38bfec730712015e8860f207", - "sha256:b6c352f4bd32dff1ea7066bd31ad0f71f8d8100b9ff709fb343f3b86cee43efe", - "sha256:c501749fdd3d6f9e726086bf0cd4437281ed47e7bca132ddb522f86a1645d360", - "sha256:c807a578a532eeb226150d5006f156632df2cc8c5693d778324b43ff8c515dd0", - "sha256:db0a40d8bcd27b4fb46676e8eb3c732c67a5a5e6bfab8927028224fbced0b40b", - "sha256:de42ea3e2943171a9e95141f2eecf972480636e8e484ccffaf1e833929e9e052", - "sha256:e95d329384717c7bf627bf27e204bc3b15c8238fa8d9d9781d93712776c14002", - "sha256:f5e236e7730cab1644e1b87aca3d2ff3e375a608542e90fe25685dae46310116", - "sha256:f888f2d2909a414680a29ae74d0592758f2b9fcdee3549887779cd4055e975db", - "sha256:fb53ffa0085002ddd43b6ca61a7b34f2d4d7c3ed66f931fe599e1a531b42af9b" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==2.1.2" - }, - "pexpect": { - "hashes": [ - "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937", - "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c" - ], - "markers": "sys_platform != 'win32'", - "version": "==4.8.0" - }, - "pg8000": { - "hashes": [ - "sha256:2fc6bf2d81d7025529e7d161f6b0202a6328e8ac5b665e77be1d72ae5cc1c980", - "sha256:3b35dabc7c2f72d403a8bb785ce28f32aeaa9493e7a72de6bce58ac575409bc1" - ], - "markers": "python_version >= '3.8'", - "version": "==1.30.2" - }, - "pickleshare": { - "hashes": [ - "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", - "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56" - ], - "version": "==0.7.5" - }, - "pkgutil-resolve-name": { - "hashes": [ - "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174", - "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e" - ], - "markers": "python_version < '3.9'", - "version": "==1.3.10" - }, - "platformdirs": { - "hashes": [ - "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3", - "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e" - ], - "markers": "python_version >= '3.7'", - "version": "==3.11.0" - }, - "pluggy": { - "hashes": [ - "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", - "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" - ], - "markers": "python_version >= '3.8'", - "version": "==1.3.0" - }, - "prefect": { - "hashes": [ - "sha256:179f179849286bb8dc0309c8718a7815e6e5fcc016398d2aea45e16fa0e3471b", - "sha256:a838d427a88845b13279b89b925e2b6acde5ff2bb090c5480617bc6047a808a8" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.4.1" - }, - "prometheus-client": { - "hashes": [ - "sha256:35f7a8c22139e2bb7ca5a698e92d38145bc8dc74c1c0bf56f25cca886a764e17", - "sha256:8de3ae2755f890826f4b6479e5571d4f74ac17a81345fe69a6778fdb92579184" - ], - "markers": "python_version >= '3.8'", - "version": "==0.18.0" - }, - "prompt-toolkit": { - "hashes": [ - "sha256:04505ade687dc26dc4284b1ad19a83be2f2afe83e7a828ace0c72f3a1df72aac", - "sha256:9dffbe1d8acf91e3de75f3b544e4842382fc06c6babe903ac9acb74dc6e08d88" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.0.39" - }, - "psutil": { - "hashes": [ - "sha256:10e8c17b4f898d64b121149afb136c53ea8b68c7531155147867b7b1ac9e7e28", - "sha256:18cd22c5db486f33998f37e2bb054cc62fd06646995285e02a51b1e08da97017", - "sha256:3ebf2158c16cc69db777e3c7decb3c0f43a7af94a60d72e87b2823aebac3d602", - "sha256:51dc3d54607c73148f63732c727856f5febec1c7c336f8f41fcbd6315cce76ac", - "sha256:6e5fb8dc711a514da83098bc5234264e551ad980cec5f85dabf4d38ed6f15e9a", - "sha256:70cb3beb98bc3fd5ac9ac617a327af7e7f826373ee64c80efd4eb2856e5051e9", - "sha256:748c9dd2583ed86347ed65d0035f45fa8c851e8d90354c122ab72319b5f366f4", - "sha256:91ecd2d9c00db9817a4b4192107cf6954addb5d9d67a969a4f436dbc9200f88c", - "sha256:92e0cc43c524834af53e9d3369245e6cc3b130e78e26100d1f63cdb0abeb3d3c", - "sha256:a6f01f03bf1843280f4ad16f4bde26b817847b4c1a0db59bf6419807bc5ce05c", - "sha256:c69596f9fc2f8acd574a12d5f8b7b1ba3765a641ea5d60fb4736bf3c08a8214a", - "sha256:ca2780f5e038379e520281e4c032dddd086906ddff9ef0d1b9dcf00710e5071c", - "sha256:daecbcbd29b289aac14ece28eca6a3e60aa361754cf6da3dfb20d4d32b6c7f57", - "sha256:e4b92ddcd7dd4cdd3f900180ea1e104932c7bce234fb88976e2a3b296441225a", - "sha256:fb8a697f11b0f5994550555fcfe3e69799e5b060c8ecf9e2f75c69302cc35c0d", - "sha256:ff18b8d1a784b810df0b0fff3bcb50ab941c3b8e2c8de5726f9c71c601c611aa" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==5.9.6" - }, - "ptyprocess": { - "hashes": [ - "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", - "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220" - ], - "markers": "os_name != 'nt'", - "version": "==0.7.0" - }, - "pure-eval": { - "hashes": [ - "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350", - "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3" - ], - "version": "==0.2.2" - }, - "pycparser": { - "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" - ], - "version": "==2.21" - }, - "pygments": { - "hashes": [ - "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692", - "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29" - ], - "markers": "python_version >= '3.7'", - "version": "==2.16.1" - }, - "pytest": { - "hashes": [ - "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac", - "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==7.4.3" - }, - "pytest-cov": { - "hashes": [ - "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6", - "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.1.0" - }, - "python-box": { - "hashes": [ - "sha256:0036fd47d388deaca8ebd65aea905f88ee6ef91d1d8ce34898b66f1824afbe80", - "sha256:1b3f346e332dba16df0b0543d319d9e7ce07d93e5ae152175302894352aa2d28", - "sha256:24c4ec0ee0278f66321100aaa9c615413da27a14ff43d376a2a3b4665e1d9494", - "sha256:2a3df244a5a79ac8f8447b5d11b5be0f2747d7b141cb2866060081ae9b53cc50", - "sha256:4ea13c98e05a3ec0ff26f254986a17290b69b5ade209fad081fd628f8fcfaa08", - "sha256:63b609555554d7a9d4b6e725f8e78ef1717c67e7d386200e03422ad612338df8", - "sha256:81ed1ec0f0ff2370227fc07277c5baca46d190a4747631bad7eb6ab1630fb7d9", - "sha256:8891735b4148e84d348c6eadd2f127152f751c9603e35d43a1f496183a291ac4", - "sha256:a0f1333c42e81529b6f68c192050df9d4505b803be7ac47f114036b98707f7cf", - "sha256:aabf8b9ae5dbc8ba431d8cbe0d4cfe737a25d52d68b0f5f2ff34915c21a2c1db", - "sha256:c046608337e723ae4de3206db5d1e1202ed166da2dfdc70c1f9361e72ace5633", - "sha256:d95e5eec4fc8f3fc5c9cc7347fc2eb4f9187c853d34c90b1658d1eff96cd4eac", - "sha256:f76b5b7f0cdc07bfdd4200dc24e6e33189bb2ae322137a2b7110fd41891a3157", - "sha256:f9266795e9c233874fb5b34fa994054b4fb0371881678e6ec45aec17fc95feac" - ], - "markers": "python_version >= '3.8'", - "version": "==7.1.1" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "python-json-logger": { - "hashes": [ - "sha256:23e7ec02d34237c5aa1e29a070193a4ea87583bb4e7f8fd06d3de8264c4b2e1c", - "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd" - ], - "markers": "python_version >= '3.6'", - "version": "==2.0.7" - }, - "python-slugify": { - "hashes": [ - "sha256:70ca6ea68fe63ecc8fa4fcf00ae651fc8a5d02d93dcd12ae6d4fc7ca46c4d395", - "sha256:ce0d46ddb668b3be82f4ed5e503dbc33dd815d83e2eb6824211310d3fb172a27" - ], - "markers": "python_version >= '3.7'", - "version": "==8.0.1" - }, - "pytz": { - "hashes": [ - "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b", - "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7" - ], - "version": "==2023.3.post1" - }, - "pytzdata": { - "hashes": [ - "sha256:3efa13b335a00a8de1d345ae41ec78dd11c9f8807f522d39850f2dd828681540", - "sha256:e1e14750bcf95016381e4d472bad004eef710f2d6417240904070b3d6654485f" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2020.1" - }, - "pyyaml": { - "hashes": [ - "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", - "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", - "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", - "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", - "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", - "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", - "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", - "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", - "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", - "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", - "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", - "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", - "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", - "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", - "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", - "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", - "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", - "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", - "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", - "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", - "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", - "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", - "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", - "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", - "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", - "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", - "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", - "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", - "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", - "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", - "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", - "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", - "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", - "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", - "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", - "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", - "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", - "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", - "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", - "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", - "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", - "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", - "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", - "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", - "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", - "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", - "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", - "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", - "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", - "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==6.0.1" - }, - "pyzmq": { - "hashes": [ - "sha256:019e59ef5c5256a2c7378f2fb8560fc2a9ff1d315755204295b2eab96b254d0a", - "sha256:034239843541ef7a1aee0c7b2cb7f6aafffb005ede965ae9cbd49d5ff4ff73cf", - "sha256:03b3f49b57264909aacd0741892f2aecf2f51fb053e7d8ac6767f6c700832f45", - "sha256:047a640f5c9c6ade7b1cc6680a0e28c9dd5a0825135acbd3569cc96ea00b2505", - "sha256:04ccbed567171579ec2cebb9c8a3e30801723c575601f9a990ab25bcac6b51e2", - "sha256:057e824b2aae50accc0f9a0570998adc021b372478a921506fddd6c02e60308e", - "sha256:11baebdd5fc5b475d484195e49bae2dc64b94a5208f7c89954e9e354fc609d8f", - "sha256:11c1d2aed9079c6b0c9550a7257a836b4a637feb334904610f06d70eb44c56d2", - "sha256:11d58723d44d6ed4dd677c5615b2ffb19d5c426636345567d6af82be4dff8a55", - "sha256:12720a53e61c3b99d87262294e2b375c915fea93c31fc2336898c26d7aed34cd", - "sha256:17ef5f01d25b67ca8f98120d5fa1d21efe9611604e8eb03a5147360f517dd1e2", - "sha256:18d43df3f2302d836f2a56f17e5663e398416e9dd74b205b179065e61f1a6edf", - "sha256:1a5d26fe8f32f137e784f768143728438877d69a586ddeaad898558dc971a5ae", - "sha256:1af379b33ef33757224da93e9da62e6471cf4a66d10078cf32bae8127d3d0d4a", - "sha256:1ccf825981640b8c34ae54231b7ed00271822ea1c6d8ba1090ebd4943759abf5", - "sha256:21eb4e609a154a57c520e3d5bfa0d97e49b6872ea057b7c85257b11e78068222", - "sha256:2243700cc5548cff20963f0ca92d3e5e436394375ab8a354bbea2b12911b20b0", - "sha256:255ca2b219f9e5a3a9ef3081512e1358bd4760ce77828e1028b818ff5610b87b", - "sha256:259c22485b71abacdfa8bf79720cd7bcf4b9d128b30ea554f01ae71fdbfdaa23", - "sha256:25f0e6b78220aba09815cd1f3a32b9c7cb3e02cb846d1cfc526b6595f6046618", - "sha256:273bc3959bcbff3f48606b28229b4721716598d76b5aaea2b4a9d0ab454ec062", - "sha256:292fe3fc5ad4a75bc8df0dfaee7d0babe8b1f4ceb596437213821f761b4589f9", - "sha256:2ca57a5be0389f2a65e6d3bb2962a971688cbdd30b4c0bd188c99e39c234f414", - "sha256:2d163a18819277e49911f7461567bda923461c50b19d169a062536fffe7cd9d2", - "sha256:2d81f1ddae3858b8299d1da72dd7d19dd36aab654c19671aa8a7e7fb02f6638a", - "sha256:2f957ce63d13c28730f7fd6b72333814221c84ca2421298f66e5143f81c9f91f", - "sha256:330f9e188d0d89080cde66dc7470f57d1926ff2fb5576227f14d5be7ab30b9fa", - "sha256:34c850ce7976d19ebe7b9d4b9bb8c9dfc7aac336c0958e2651b88cbd46682123", - "sha256:35b5ab8c28978fbbb86ea54958cd89f5176ce747c1fb3d87356cf698048a7790", - "sha256:3669cf8ee3520c2f13b2e0351c41fea919852b220988d2049249db10046a7afb", - "sha256:381469297409c5adf9a0e884c5eb5186ed33137badcbbb0560b86e910a2f1e76", - "sha256:3d0a409d3b28607cc427aa5c30a6f1e4452cc44e311f843e05edb28ab5e36da0", - "sha256:44e58a0554b21fc662f2712814a746635ed668d0fbc98b7cb9d74cb798d202e6", - "sha256:458dea649f2f02a0b244ae6aef8dc29325a2810aa26b07af8374dc2a9faf57e3", - "sha256:48e466162a24daf86f6b5ca72444d2bf39a5e58da5f96370078be67c67adc978", - "sha256:49d238cf4b69652257db66d0c623cd3e09b5d2e9576b56bc067a396133a00d4a", - "sha256:4ca1ed0bb2d850aa8471387882247c68f1e62a4af0ce9c8a1dbe0d2bf69e41fb", - "sha256:52533489f28d62eb1258a965f2aba28a82aa747202c8fa5a1c7a43b5db0e85c1", - "sha256:548d6482dc8aadbe7e79d1b5806585c8120bafa1ef841167bc9090522b610fa6", - "sha256:5619f3f5a4db5dbb572b095ea3cb5cc035335159d9da950830c9c4db2fbb6995", - "sha256:57459b68e5cd85b0be8184382cefd91959cafe79ae019e6b1ae6e2ba8a12cda7", - "sha256:5a34d2395073ef862b4032343cf0c32a712f3ab49d7ec4f42c9661e0294d106f", - "sha256:61706a6b6c24bdece85ff177fec393545a3191eeda35b07aaa1458a027ad1304", - "sha256:724c292bb26365659fc434e9567b3f1adbdb5e8d640c936ed901f49e03e5d32e", - "sha256:73461eed88a88c866656e08f89299720a38cb4e9d34ae6bf5df6f71102570f2e", - "sha256:76705c9325d72a81155bb6ab48d4312e0032bf045fb0754889133200f7a0d849", - "sha256:76c1c8efb3ca3a1818b837aea423ff8a07bbf7aafe9f2f6582b61a0458b1a329", - "sha256:77a41c26205d2353a4c94d02be51d6cbdf63c06fbc1295ea57dad7e2d3381b71", - "sha256:79986f3b4af059777111409ee517da24a529bdbd46da578b33f25580adcff728", - "sha256:7cff25c5b315e63b07a36f0c2bab32c58eafbe57d0dce61b614ef4c76058c115", - "sha256:7f7e58effd14b641c5e4dec8c7dab02fb67a13df90329e61c869b9cc607ef752", - "sha256:820c4a08195a681252f46926de10e29b6bbf3e17b30037bd4250d72dd3ddaab8", - "sha256:87e34f31ca8f168c56d6fbf99692cc8d3b445abb5bfd08c229ae992d7547a92a", - "sha256:8f03d3f0d01cb5a018debeb412441996a517b11c5c17ab2001aa0597c6d6882c", - "sha256:90f26dc6d5f241ba358bef79be9ce06de58d477ca8485e3291675436d3827cf8", - "sha256:955215ed0604dac5b01907424dfa28b40f2b2292d6493445dd34d0dfa72586a8", - "sha256:985bbb1316192b98f32e25e7b9958088431d853ac63aca1d2c236f40afb17c83", - "sha256:a382372898a07479bd34bda781008e4a954ed8750f17891e794521c3e21c2e1c", - "sha256:a882ac0a351288dd18ecae3326b8a49d10c61a68b01419f3a0b9a306190baf69", - "sha256:aa8d6cdc8b8aa19ceb319aaa2b660cdaccc533ec477eeb1309e2a291eaacc43a", - "sha256:abc719161780932c4e11aaebb203be3d6acc6b38d2f26c0f523b5b59d2fc1996", - "sha256:abf34e43c531bbb510ae7e8f5b2b1f2a8ab93219510e2b287a944432fad135f3", - "sha256:ade6d25bb29c4555d718ac6d1443a7386595528c33d6b133b258f65f963bb0f6", - "sha256:afea96f64efa98df4da6958bae37f1cbea7932c35878b185e5982821bc883369", - "sha256:b1579413ae492b05de5a6174574f8c44c2b9b122a42015c5292afa4be2507f28", - "sha256:b3451108ab861040754fa5208bca4a5496c65875710f76789a9ad27c801a0075", - "sha256:b9af3757495c1ee3b5c4e945c1df7be95562277c6e5bccc20a39aec50f826cd0", - "sha256:bc16ac425cc927d0a57d242589f87ee093884ea4804c05a13834d07c20db203c", - "sha256:c2910967e6ab16bf6fbeb1f771c89a7050947221ae12a5b0b60f3bca2ee19bca", - "sha256:c2b92812bd214018e50b6380ea3ac0c8bb01ac07fcc14c5f86a5bb25e74026e9", - "sha256:c2f20ce161ebdb0091a10c9ca0372e023ce24980d0e1f810f519da6f79c60800", - "sha256:c56d748ea50215abef7030c72b60dd723ed5b5c7e65e7bc2504e77843631c1a6", - "sha256:c7c133e93b405eb0d36fa430c94185bdd13c36204a8635470cccc200723c13bb", - "sha256:c9c6c9b2c2f80747a98f34ef491c4d7b1a8d4853937bb1492774992a120f475d", - "sha256:cbc8df5c6a88ba5ae385d8930da02201165408dde8d8322072e3e5ddd4f68e22", - "sha256:cff084c6933680d1f8b2f3b4ff5bbb88538a4aac00d199ac13f49d0698727ecb", - "sha256:d2045d6d9439a0078f2a34b57c7b18c4a6aef0bee37f22e4ec9f32456c852c71", - "sha256:d20a0ddb3e989e8807d83225a27e5c2eb2260eaa851532086e9e0fa0d5287d83", - "sha256:d457aed310f2670f59cc5b57dcfced452aeeed77f9da2b9763616bd57e4dbaae", - "sha256:d89528b4943d27029a2818f847c10c2cecc79fa9590f3cb1860459a5be7933eb", - "sha256:db0b2af416ba735c6304c47f75d348f498b92952f5e3e8bff449336d2728795d", - "sha256:deee9ca4727f53464daf089536e68b13e6104e84a37820a88b0a057b97bba2d2", - "sha256:df27ffddff4190667d40de7beba4a950b5ce78fe28a7dcc41d6f8a700a80a3c0", - "sha256:e0c95ddd4f6e9fca4e9e3afaa4f9df8552f0ba5d1004e89ef0a68e1f1f9807c7", - "sha256:e1c1be77bc5fb77d923850f82e55a928f8638f64a61f00ff18a67c7404faf008", - "sha256:e1ffa1c924e8c72778b9ccd386a7067cddf626884fd8277f503c48bb5f51c762", - "sha256:e2400a94f7dd9cb20cd012951a0cbf8249e3d554c63a9c0cdfd5cbb6c01d2dec", - "sha256:e61f091c3ba0c3578411ef505992d356a812fb200643eab27f4f70eed34a29ef", - "sha256:e8a701123029cc240cea61dd2d16ad57cab4691804143ce80ecd9286b464d180", - "sha256:eadbefd5e92ef8a345f0525b5cfd01cf4e4cc651a2cffb8f23c0dd184975d787", - "sha256:f32260e556a983bc5c7ed588d04c942c9a8f9c2e99213fec11a031e316874c7e", - "sha256:f8115e303280ba09f3898194791a153862cbf9eef722ad8f7f741987ee2a97c7", - "sha256:fedbdc753827cf014c01dbbee9c3be17e5a208dcd1bf8641ce2cd29580d1f0d4" - ], - "markers": "python_version >= '3.6'", - "version": "==25.1.1" - }, - "referencing": { - "hashes": [ - "sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf", - "sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0" - ], - "markers": "python_version >= '3.8'", - "version": "==0.30.2" - }, - "requests": { - "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" - ], - "markers": "python_version >= '3.7'", - "version": "==2.31.0" - }, - "rfc3339-validator": { - "hashes": [ - "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", - "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.1.4" - }, - "rfc3986-validator": { - "hashes": [ - "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", - "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.1.1" - }, - "rpds-py": { - "hashes": [ - "sha256:023574366002bf1bd751ebaf3e580aef4a468b3d3c216d2f3f7e16fdabd885ed", - "sha256:031f76fc87644a234883b51145e43985aa2d0c19b063e91d44379cd2786144f8", - "sha256:052a832078943d2b2627aea0d19381f607fe331cc0eb5df01991268253af8417", - "sha256:0699ab6b8c98df998c3eacf51a3b25864ca93dab157abe358af46dc95ecd9801", - "sha256:0713631d6e2d6c316c2f7b9320a34f44abb644fc487b77161d1724d883662e31", - "sha256:0774a46b38e70fdde0c6ded8d6d73115a7c39d7839a164cc833f170bbf539116", - "sha256:0898173249141ee99ffcd45e3829abe7bcee47d941af7434ccbf97717df020e5", - "sha256:09586f51a215d17efdb3a5f090d7cbf1633b7f3708f60a044757a5d48a83b393", - "sha256:102eac53bb0bf0f9a275b438e6cf6904904908562a1463a6fc3323cf47d7a532", - "sha256:10f32b53f424fc75ff7b713b2edb286fdbfc94bf16317890260a81c2c00385dc", - "sha256:150eec465dbc9cbca943c8e557a21afdcf9bab8aaabf386c44b794c2f94143d2", - "sha256:1d7360573f1e046cb3b0dceeb8864025aa78d98be4bb69f067ec1c40a9e2d9df", - "sha256:1f36a9d751f86455dc5278517e8b65580eeee37d61606183897f122c9e51cef3", - "sha256:24656dc36f866c33856baa3ab309da0b6a60f37d25d14be916bd3e79d9f3afcf", - "sha256:25860ed5c4e7f5e10c496ea78af46ae8d8468e0be745bd233bab9ca99bfd2647", - "sha256:26857f0f44f0e791f4a266595a7a09d21f6b589580ee0585f330aaccccb836e3", - "sha256:2bb2e4826be25e72013916eecd3d30f66fd076110de09f0e750163b416500721", - "sha256:2f6da6d842195fddc1cd34c3da8a40f6e99e4a113918faa5e60bf132f917c247", - "sha256:30adb75ecd7c2a52f5e76af50644b3e0b5ba036321c390b8e7ec1bb2a16dd43c", - "sha256:3339eca941568ed52d9ad0f1b8eb9fe0958fa245381747cecf2e9a78a5539c42", - "sha256:34ad87a831940521d462ac11f1774edf867c34172010f5390b2f06b85dcc6014", - "sha256:3777cc9dea0e6c464e4b24760664bd8831738cc582c1d8aacf1c3f546bef3f65", - "sha256:3953c6926a63f8ea5514644b7afb42659b505ece4183fdaaa8f61d978754349e", - "sha256:3c4eff26eddac49d52697a98ea01b0246e44ca82ab09354e94aae8823e8bda02", - "sha256:40578a6469e5d1df71b006936ce95804edb5df47b520c69cf5af264d462f2cbb", - "sha256:40f93086eef235623aa14dbddef1b9fb4b22b99454cb39a8d2e04c994fb9868c", - "sha256:4134aa2342f9b2ab6c33d5c172e40f9ef802c61bb9ca30d21782f6e035ed0043", - "sha256:442626328600bde1d09dc3bb00434f5374948838ce75c41a52152615689f9403", - "sha256:4a5ee600477b918ab345209eddafde9f91c0acd931f3776369585a1c55b04c57", - "sha256:4ce5a708d65a8dbf3748d2474b580d606b1b9f91b5c6ab2a316e0b0cf7a4ba50", - "sha256:516a611a2de12fbea70c78271e558f725c660ce38e0006f75139ba337d56b1f6", - "sha256:52c215eb46307c25f9fd2771cac8135d14b11a92ae48d17968eda5aa9aaf5071", - "sha256:53c43e10d398e365da2d4cc0bcaf0854b79b4c50ee9689652cdc72948e86f487", - "sha256:5752b761902cd15073a527b51de76bbae63d938dc7c5c4ad1e7d8df10e765138", - "sha256:5e8a78bd4879bff82daef48c14d5d4057f6856149094848c3ed0ecaf49f5aec2", - "sha256:5ed505ec6305abd2c2c9586a7b04fbd4baf42d4d684a9c12ec6110deefe2a063", - "sha256:5ee97c683eaface61d38ec9a489e353d36444cdebb128a27fe486a291647aff6", - "sha256:61fa268da6e2e1cd350739bb61011121fa550aa2545762e3dc02ea177ee4de35", - "sha256:64ccc28683666672d7c166ed465c09cee36e306c156e787acef3c0c62f90da5a", - "sha256:66414dafe4326bca200e165c2e789976cab2587ec71beb80f59f4796b786a238", - "sha256:68fe9199184c18d997d2e4293b34327c0009a78599ce703e15cd9a0f47349bba", - "sha256:6a555ae3d2e61118a9d3e549737bb4a56ff0cec88a22bd1dfcad5b4e04759175", - "sha256:6bdc11f9623870d75692cc33c59804b5a18d7b8a4b79ef0b00b773a27397d1f6", - "sha256:6cf4393c7b41abbf07c88eb83e8af5013606b1cdb7f6bc96b1b3536b53a574b8", - "sha256:6eef672de005736a6efd565577101277db6057f65640a813de6c2707dc69f396", - "sha256:734c41f9f57cc28658d98270d3436dba65bed0cfc730d115b290e970150c540d", - "sha256:73e0a78a9b843b8c2128028864901f55190401ba38aae685350cf69b98d9f7c9", - "sha256:775049dfa63fb58293990fc59473e659fcafd953bba1d00fc5f0631a8fd61977", - "sha256:7854a207ef77319ec457c1eb79c361b48807d252d94348305db4f4b62f40f7f3", - "sha256:78ca33811e1d95cac8c2e49cb86c0fb71f4d8409d8cbea0cb495b6dbddb30a55", - "sha256:79edd779cfc46b2e15b0830eecd8b4b93f1a96649bcb502453df471a54ce7977", - "sha256:7bf347b495b197992efc81a7408e9a83b931b2f056728529956a4d0858608b80", - "sha256:7fde6d0e00b2fd0dbbb40c0eeec463ef147819f23725eda58105ba9ca48744f4", - "sha256:81de24a1c51cfb32e1fbf018ab0bdbc79c04c035986526f76c33e3f9e0f3356c", - "sha256:879fb24304ead6b62dbe5034e7b644b71def53c70e19363f3c3be2705c17a3b4", - "sha256:8e7f2219cb72474571974d29a191714d822e58be1eb171f229732bc6fdedf0ac", - "sha256:9164ec8010327ab9af931d7ccd12ab8d8b5dc2f4c6a16cbdd9d087861eaaefa1", - "sha256:945eb4b6bb8144909b203a88a35e0a03d22b57aefb06c9b26c6e16d72e5eb0f0", - "sha256:99a57006b4ec39dbfb3ed67e5b27192792ffb0553206a107e4aadb39c5004cd5", - "sha256:9e9184fa6c52a74a5521e3e87badbf9692549c0fcced47443585876fcc47e469", - "sha256:9ff93d3aedef11f9c4540cf347f8bb135dd9323a2fc705633d83210d464c579d", - "sha256:a360cfd0881d36c6dc271992ce1eda65dba5e9368575663de993eeb4523d895f", - "sha256:a5d7ed104d158c0042a6a73799cf0eb576dfd5fc1ace9c47996e52320c37cb7c", - "sha256:ac17044876e64a8ea20ab132080ddc73b895b4abe9976e263b0e30ee5be7b9c2", - "sha256:ad857f42831e5b8d41a32437f88d86ead6c191455a3499c4b6d15e007936d4cf", - "sha256:b2039f8d545f20c4e52713eea51a275e62153ee96c8035a32b2abb772b6fc9e5", - "sha256:b455492cab07107bfe8711e20cd920cc96003e0da3c1f91297235b1603d2aca7", - "sha256:b4a9fe992887ac68256c930a2011255bae0bf5ec837475bc6f7edd7c8dfa254e", - "sha256:b5a53f5998b4bbff1cb2e967e66ab2addc67326a274567697379dd1e326bded7", - "sha256:b788276a3c114e9f51e257f2a6f544c32c02dab4aa7a5816b96444e3f9ffc336", - "sha256:bddd4f91eede9ca5275e70479ed3656e76c8cdaaa1b354e544cbcf94c6fc8ac4", - "sha256:c0503c5b681566e8b722fe8c4c47cce5c7a51f6935d5c7012c4aefe952a35eed", - "sha256:c1b3cd23d905589cb205710b3988fc8f46d4a198cf12862887b09d7aaa6bf9b9", - "sha256:c48f3fbc3e92c7dd6681a258d22f23adc2eb183c8cb1557d2fcc5a024e80b094", - "sha256:c63c3ef43f0b3fb00571cff6c3967cc261c0ebd14a0a134a12e83bdb8f49f21f", - "sha256:c6c45a2d2b68c51fe3d9352733fe048291e483376c94f7723458cfd7b473136b", - "sha256:caa1afc70a02645809c744eefb7d6ee8fef7e2fad170ffdeacca267fd2674f13", - "sha256:cc435d059f926fdc5b05822b1be4ff2a3a040f3ae0a7bbbe672babb468944722", - "sha256:cf693eb4a08eccc1a1b636e4392322582db2a47470d52e824b25eca7a3977b53", - "sha256:cf71343646756a072b85f228d35b1d7407da1669a3de3cf47f8bbafe0c8183a4", - "sha256:d08f63561c8a695afec4975fae445245386d645e3e446e6f260e81663bfd2e38", - "sha256:d29ddefeab1791e3c751e0189d5f4b3dbc0bbe033b06e9c333dca1f99e1d523e", - "sha256:d7f5e15c953ace2e8dde9824bdab4bec50adb91a5663df08d7d994240ae6fa31", - "sha256:d858532212f0650be12b6042ff4378dc2efbb7792a286bee4489eaa7ba010586", - "sha256:d97dd44683802000277bbf142fd9f6b271746b4846d0acaf0cefa6b2eaf2a7ad", - "sha256:dcdc88b6b01015da066da3fb76545e8bb9a6880a5ebf89e0f0b2e3ca557b3ab7", - "sha256:dd609fafdcdde6e67a139898196698af37438b035b25ad63704fd9097d9a3482", - "sha256:defa2c0c68734f4a82028c26bcc85e6b92cced99866af118cd6a89b734ad8e0d", - "sha256:e22260a4741a0e7a206e175232867b48a16e0401ef5bce3c67ca5b9705879066", - "sha256:e225a6a14ecf44499aadea165299092ab0cba918bb9ccd9304eab1138844490b", - "sha256:e3df0bc35e746cce42579826b89579d13fd27c3d5319a6afca9893a9b784ff1b", - "sha256:e6fcc026a3f27c1282c7ed24b7fcac82cdd70a0e84cc848c0841a3ab1e3dea2d", - "sha256:e782379c2028a3611285a795b89b99a52722946d19fc06f002f8b53e3ea26ea9", - "sha256:e8cdd52744f680346ff8c1ecdad5f4d11117e1724d4f4e1874f3a67598821069", - "sha256:e9616f5bd2595f7f4a04b67039d890348ab826e943a9bfdbe4938d0eba606971", - "sha256:e98c4c07ee4c4b3acf787e91b27688409d918212dfd34c872201273fdd5a0e18", - "sha256:ebdab79f42c5961682654b851f3f0fc68e6cc7cd8727c2ac4ffff955154123c1", - "sha256:f0f17f2ce0f3529177a5fff5525204fad7b43dd437d017dd0317f2746773443d", - "sha256:f4e56860a5af16a0fcfa070a0a20c42fbb2012eed1eb5ceeddcc7f8079214281" - ], - "markers": "python_version >= '3.8'", - "version": "==0.10.6" - }, - "scramp": { - "hashes": [ - "sha256:b142312df7c2977241d951318b7ee923d6b7a4f75ba0f05b621ece1ed616faa3", - "sha256:b7022a140040f33cf863ab2657917ed05287a807b917950489b89b9f685d59bc" - ], - "markers": "python_version >= '3.7'", - "version": "==1.4.4" - }, - "send2trash": { - "hashes": [ - "sha256:a384719d99c07ce1eefd6905d2decb6f8b7ed054025bb0e618919f945de4f679", - "sha256:c132d59fa44b9ca2b1699af5c86f57ce9f4c5eb56629d5d55fbb7a35f84e2312" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.8.2" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "sniffio": { - "hashes": [ - "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101", - "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384" - ], - "markers": "python_version >= '3.7'", - "version": "==1.3.0" - }, - "sortedcontainers": { - "hashes": [ - "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", - "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" - ], - "version": "==2.4.0" - }, - "soupsieve": { - "hashes": [ - "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690", - "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7" - ], - "markers": "python_version >= '3.8'", - "version": "==2.5" - }, - "sqlalchemy": { - "hashes": [ - "sha256:0b7dbe6369677a2bea68fe9812c6e4bbca06ebfa4b5cde257b2b0bf208709131", - "sha256:128a948bd40780667114b0297e2cc6d657b71effa942e0a368d8cc24293febb3", - "sha256:14b0cacdc8a4759a1e1bd47dc3ee3f5db997129eb091330beda1da5a0e9e5bd7", - "sha256:1fb9cb60e0f33040e4f4681e6658a7eb03b5cb4643284172f91410d8c493dace", - "sha256:273505fcad22e58cc67329cefab2e436006fc68e3c5423056ee0513e6523268a", - "sha256:2e70e0673d7d12fa6cd363453a0d22dac0d9978500aa6b46aa96e22690a55eab", - "sha256:34e1c5d9cd3e6bf3d1ce56971c62a40c06bfc02861728f368dcfec8aeedb2814", - "sha256:3b97ddf509fc21e10b09403b5219b06c5b558b27fc2453150274fa4e70707dbf", - "sha256:3f6997da81114daef9203d30aabfa6b218a577fc2bd797c795c9c88c9eb78d49", - "sha256:82dd4131d88395df7c318eeeef367ec768c2a6fe5bd69423f7720c4edb79473c", - "sha256:85292ff52ddf85a39367057c3d7968a12ee1fb84565331a36a8fead346f08796", - "sha256:8a7a66297e46f85a04d68981917c75723e377d2e0599d15fbe7a56abed5e2d75", - "sha256:8b881ac07d15fb3e4f68c5a67aa5cdaf9eb8f09eb5545aaf4b0a5f5f4659be18", - "sha256:a3257a6e09626d32b28a0c5b4f1a97bced585e319cfa90b417f9ab0f6145c33c", - "sha256:a9bddb60566dc45c57fd0a5e14dd2d9e5f106d2241e0a2dc0c1da144f9444516", - "sha256:bdb77e1789e7596b77fd48d99ec1d2108c3349abd20227eea0d48d3f8cf398d9", - "sha256:c1db0221cb26d66294f4ca18c533e427211673ab86c1fbaca8d6d9ff78654293", - "sha256:c4cb501d585aa74a0f86d0ea6263b9c5e1d1463f8f9071392477fd401bd3c7cc", - "sha256:d00665725063692c42badfd521d0c4392e83c6c826795d38eb88fb108e5660e5", - "sha256:d0fed0f791d78e7767c2db28d34068649dfeea027b83ed18c45a423f741425cb", - "sha256:d69738d582e3a24125f0c246ed8d712b03bd21e148268421e4a4d09c34f521a5", - "sha256:db4db3c08ffbb18582f856545f058a7a5e4ab6f17f75795ca90b3c38ee0a8ba4", - "sha256:f1fcee5a2c859eecb4ed179edac5ffbc7c84ab09a5420219078ccc6edda45436", - "sha256:f2d526aeea1bd6a442abc7c9b4b00386fd70253b80d54a0930c0a216230a35be", - "sha256:fbaf6643a604aa17e7a7afd74f665f9db882df5c297bdd86c38368f2c471f37d" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.4.50" - }, - "sqlalchemy-utils": { - "hashes": [ - "sha256:6c96b0768ea3f15c0dc56b363d386138c562752b84f647fb8d31a2223aaab801", - "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==0.41.1" - }, - "stack-data": { - "hashes": [ - "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", - "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695" - ], - "version": "==0.6.3" - }, - "tabulate": { - "hashes": [ - "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", - "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f" - ], - "markers": "python_version >= '3.7'", - "version": "==0.9.0" - }, - "tblib": { - "hashes": [ - "sha256:80a6c77e59b55e83911e1e607c649836a69c103963c5f28a46cbeef44acf8129", - "sha256:93622790a0a29e04f0346458face1e144dc4d32f493714c6c3dff82a4adb77e6" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, - "terminado": { - "hashes": [ - "sha256:6ccbbcd3a4f8a25a5ec04991f39a0b8db52dfcd487ea0e578d977e6752380333", - "sha256:8650d44334eba354dd591129ca3124a6ba42c3d5b70df5051b6921d506fdaeae" - ], - "markers": "python_version >= '3.7'", - "version": "==0.17.1" - }, - "testing-postgresql": { - "hashes": [ - "sha256:1b41daeb98dfc8cd4a584bb91e8f5f4ab182993870f95257afe5f1ba6151a598", - "sha256:8e1a69760369a7a8ffe63a66b6d95a5cd82db2fb976e4a8f85ffd24fbfc447d8" - ], - "index": "pypi", - "version": "==1.3.0" - }, - "testing.common.database": { - "hashes": [ - "sha256:965d80b2985315325dc358c3061b174a712f4d4d5bf6a80b58b11f9a1dd86d73", - "sha256:e3ed492bf480a87f271f74c53b262caf5d85c8bc09989a8f534fa2283ec52492" - ], - "version": "==2.0.3" - }, - "text-unidecode": { - "hashes": [ - "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", - "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93" - ], - "version": "==1.3" - }, - "tinycss2": { - "hashes": [ - "sha256:2b80a96d41e7c3914b8cda8bc7f705a4d9c49275616e886103dd839dfc847847", - "sha256:8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627" - ], - "markers": "python_version >= '3.7'", - "version": "==1.2.1" - }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.2" - }, - "tomli": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_version < '3.11'", - "version": "==2.0.1" - }, - "toolz": { - "hashes": [ - "sha256:2059bd4148deb1884bb0eb770a3cde70e7f954cfbbdc2285f1f2de01fd21eb6f", - "sha256:88c570861c440ee3f2f6037c4654613228ff40c93a6c25e0eba70d17282c6194" - ], - "markers": "python_version >= '3.5'", - "version": "==0.12.0" - }, - "tornado": { - "hashes": [ - "sha256:1bd19ca6c16882e4d37368e0152f99c099bad93e0950ce55e71daed74045908f", - "sha256:22d3c2fa10b5793da13c807e6fc38ff49a4f6e1e3868b0a6f4164768bb8e20f5", - "sha256:502fba735c84450974fec147340016ad928d29f1e91f49be168c0a4c18181e1d", - "sha256:65ceca9500383fbdf33a98c0087cb975b2ef3bfb874cb35b8de8740cf7f41bd3", - "sha256:71a8db65160a3c55d61839b7302a9a400074c9c753040455494e2af74e2501f2", - "sha256:7ac51f42808cca9b3613f51ffe2a965c8525cb1b00b7b2d56828b8045354f76a", - "sha256:7d01abc57ea0dbb51ddfed477dfe22719d376119844e33c661d873bf9c0e4a16", - "sha256:805d507b1f588320c26f7f097108eb4023bbaa984d63176d1652e184ba24270a", - "sha256:9dc4444c0defcd3929d5c1eb5706cbe1b116e762ff3e0deca8b715d14bf6ec17", - "sha256:ceb917a50cd35882b57600709dd5421a418c29ddc852da8bcdab1f0db33406b0", - "sha256:e7d8db41c0181c80d76c982aacc442c0783a2c54d6400fe028954201a2e032fe" - ], - "markers": "python_version >= '3.8'", - "version": "==6.3.3" - }, - "traitlets": { - "hashes": [ - "sha256:9b232b9430c8f57288c1024b34a8f0251ddcc47268927367a0dd3eeaca40deb5", - "sha256:baf991e61542da48fe8aef8b779a9ea0aa38d8a54166ee250d5af5ecf4486619" - ], - "markers": "python_version >= '3.8'", - "version": "==5.13.0" - }, - "types-python-dateutil": { - "hashes": [ - "sha256:1f4f10ac98bb8b16ade9dbee3518d9ace017821d94b057a425b069f834737f4b", - "sha256:f977b8de27787639986b4e28963263fd0e5158942b3ecef91b9335c130cb1ce9" - ], - "version": "==2.8.19.14" - }, - "typing-extensions": { - "hashes": [ - "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", - "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" - ], - "markers": "python_version < '3.10'", - "version": "==4.8.0" - }, - "uri-template": { - "hashes": [ - "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", - "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363" - ], - "version": "==1.3.0" - }, - "urllib3": { - "hashes": [ - "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", - "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.7" - }, - "wcwidth": { - "hashes": [ - "sha256:9a929bd8380f6cd9571a968a9c8f4353ca58d7cd812a4822bba831f8d685b223", - "sha256:a675d1a4a2d24ef67096a04b85b02deeecd8e226f57b5e3a72dbb9ed99d27da8" - ], - "version": "==0.2.9" - }, - "webcolors": { - "hashes": [ - "sha256:29bc7e8752c0a1bd4a1f03c14d6e6a72e93d82193738fa860cbff59d0fcc11bf", - "sha256:c225b674c83fa923be93d235330ce0300373d02885cef23238813b0d5668304a" - ], - "version": "==1.13" - }, - "webencodings": { - "hashes": [ - "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", - "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" - ], - "version": "==0.5.1" - }, - "websocket-client": { - "hashes": [ - "sha256:084072e0a7f5f347ef2ac3d8698a5e0b4ffbfcab607628cadabc650fc9a83a24", - "sha256:b3324019b3c28572086c4a319f91d1dcd44e6e11cd340232978c684a7650d0df" - ], - "markers": "python_version >= '3.8'", - "version": "==1.6.4" - }, - "widgetsnbextension": { - "hashes": [ - "sha256:3c1f5e46dc1166dfd40a42d685e6a51396fd34ff878742a3e47c6f0cc4a2a385", - "sha256:91452ca8445beb805792f206e560c1769284267a30ceb1cec9f5bcc887d15175" - ], - "markers": "python_version >= '3.7'", - "version": "==4.0.9" - }, - "zict": { - "hashes": [ - "sha256:5796e36bd0e0cc8cf0fbc1ace6a68912611c1dbd74750a3f3026b9b9d6a327ae", - "sha256:e321e263b6a97aafc0790c3cfb3c04656b7066e6738c37fffcca95d803c9fba5" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, - "zipp": { - "hashes": [ - "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31", - "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0" - ], - "markers": "python_version >= '3.8'", - "version": "==3.17.0" - } - } -} diff --git a/autoflow/README.md b/autoflow/README.md deleted file mode 100644 index f88c499d91..0000000000 --- a/autoflow/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# AutoFlow - -AutoFlow is a tool that automates the event-driven execution of workflows consisting of Jupyter notebooks that interact with FlowKit via FlowAPI. Workflows can consist of multiple inter-dependent notebooks, and can run automatically for each new date of CDR data available in FlowDB. After execution, notebooks can optionally be converted to PDF reports. - -AutoFlow uses: -- [Prefect](https://github.com/prefecthq/prefect) to define and run workflows, -- [Papermill](https://github.com/nteract/papermill) to parametrise and execute Jupyter notebooks, -- [Scrapbook](https://github.com/nteract/scrapbook) to enable data-sharing between notebooks, -- [nbconvert](https://github.com/jupyter/nbconvert) and [asciidoctor-pdf](https://github.com/asciidoctor/asciidoctor-pdf) to convert notebooks to PDF, via asciidoc. - -Documentation for AutoFlow can be found [here](https://flowkit.xyz/analyst/autoflow/). - -## Example - -`autoflow/examples/` contains an example AutoFlow workflow, which runs two Jupyter notebooks and produces a PDF report showing in/outflows above normal for each day of CDR data. `autoflow/examples/inputs/workflows.yml` configures the available dates sensor to check for new dates every day at midnight, and run the flows-above-normal workflow with two different sets of parameters. - -The AutoFlow example can be run from the FlowKit root by first creating a FlowAPI token, and then running - -```bash -set -a && . development_environment && set +a -export FLOWAPI_TOKEN= -make up DOCKER_SERVICES="flowdb_testdata flowmachine flowmachine_query_locker flowapi autoflow" -``` - -Outputs will be in `autoflow/examples/outputs/`. - -## Contents - -- `autoflow/` - Python package for running notebooks workflows. Contains the following modules: - - `__main__.py` - Main script that runs `app.main()`. Run using `python -m autoflow`. - - `app.py` - Defines the `main` function, that initialises the database, parses the input file and runs workflows. - - `date_stencil.py` - Defines a `DateStencil` class to represent date stencils. - - `model.py` - Defines a database model for storing workflow run metadata. - - `parser.py` - Functions for parsing workflow definition files. - - `sensor.py` - Defines the `available_dates_sensor` prefect flow, which can be configured to run other workflows when new days of data become available. - - `utils.py` - Various utility functions. - - `workflows.py` - Prefect tasks used in workflows, and a `make_notebooks_workflow` function to create a prefect flow that parametrises and executes notebooks. -- `config/` - Directory containing the following configuration files: - - `config.toml` - Prefect user configuration file. Defines config values available to prefect tasks during execution. - - `asciidoc_extended.tpl` - Extends the default `nbconvert` asciidoc template. This template will be used when converting notebooks to PDF, unless the user provides a different template. -- `examples/` - Contains inputs for an example date-triggered workflow that produces a PDF report of flows above normal for each day of CDR data. Running `make autoflow-up` in the FlowKit root directory will start a AutoFlow container that runs this example workflow. - - `inputs/` - The `AUTOFLOW_INPUTS_DIR` environment variable should point to this directory when running the example. It contains: - - `run_flows.ipynb` - Notebook that runs two `flows` queries, so that they are stored in cache. The query IDs for the `flows` queries are glued in this notebook. - - `flows_report.ipynb` - Notebook that reads the query IDs from `run_flows.ipynb`, gets the query results using FlowClient and combines them with geography data, and displays the flows above normal in plots and tables. This notebook is designed to be converted to a PDF report after execution. - - `workflows.yml` - Yaml file that defines a workflow that runs `run_flows.ipynb` followed by `flows_report.ipynb` and creates a PDF report from `flows_report.ipynb`, and configures the available dates sensor to run this workflow. - - `outputs/` - The `AUTOFLOW_OUTPUTS_DIR` environment variable should point to this directory when running the example. Executed notebooks and PDF reports will be saved to subdirectories `notebooks/` and `reports/`, respectively. -- `tests/` - Unit tests. -- `Dockerfile` - Dockerfile for building the `flowminder/autoflow` image. -- `docker-stack.yml` - Example docker stack file that could be used to start AutoFlow using docker secrets. Note that for real usage this docker stack would also need to be connected to a network on which a FlowAPI is accessible. -- `Pipfile` - Pipfile that can be used to create a pipenv environment for running AutoFlow locally (i.e. outside a container). The Pipfile is not used when building the docker image. diff --git a/autoflow/autoflow/__init__.py b/autoflow/autoflow/__init__.py deleted file mode 100644 index 3e25ee20da..0000000000 --- a/autoflow/autoflow/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from ._version import get_versions - -__version__ = get_versions()["version"] -del get_versions - -from . import _version - -__version__ = _version.get_versions()["version"] diff --git a/autoflow/autoflow/__main__.py b/autoflow/autoflow/__main__.py deleted file mode 100644 index 4149b8c9e6..0000000000 --- a/autoflow/autoflow/__main__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Main script, that will be executed when running `python -m autoflow`. -""" - -from autoflow import app - - -if __name__ == "__main__": - app.main() diff --git a/autoflow/autoflow/_version.py b/autoflow/autoflow/_version.py deleted file mode 100644 index dd6a13686b..0000000000 --- a/autoflow/autoflow/_version.py +++ /dev/null @@ -1,692 +0,0 @@ -# This file helps to compute a version number in source trees obtained from -# git-archive tarball (such as those provided by githubs download-from-tag -# feature). Distribution tarballs (built by setup.py sdist) and build -# directories (produced by setup.py build) will contain a much shorter file -# that just contains the computed version number. - -# This file is released into the public domain. Generated by -# versioneer-0.23 (https://github.com/python-versioneer/python-versioneer) - -"""Git implementation of _version.py.""" - -import errno -import os -import re -import subprocess -import sys -from typing import Callable, Dict -import functools - - -def get_keywords(): - """Get the keywords needed to look up the version information.""" - # these strings will be replaced by git during git-archive. - # setup.py/versioneer.py will grep for the variable names, so they must - # each be defined on a line of their own. _version.py will just call - # get_keywords(). - git_refnames = "$Format:%d$" - git_full = "$Format:%H$" - git_date = "$Format:%ci$" - keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} - return keywords - - -class VersioneerConfig: - """Container for Versioneer configuration parameters.""" - - -def get_config(): - """Create, populate and return the VersioneerConfig() object.""" - # these strings are filled in when 'setup.py versioneer' creates - # _version.py - cfg = VersioneerConfig() - cfg.VCS = "git" - cfg.style = "pep440-pre" - cfg.tag_prefix = "" - cfg.parentdir_prefix = "FlowKit-" - cfg.versionfile_source = "autoflow/_version.py" - cfg.verbose = False - return cfg - - -class NotThisMethod(Exception): - """Exception raised if a method is not valid for the current scenario.""" - - -LONG_VERSION_PY: Dict[str, str] = {} -HANDLERS: Dict[str, Dict[str, Callable]] = {} - - -def register_vcs_handler(vcs, method): # decorator - """Create decorator to mark a method as the handler of a VCS.""" - - def decorate(f): - """Store f in HANDLERS[vcs][method].""" - if vcs not in HANDLERS: - HANDLERS[vcs] = {} - HANDLERS[vcs][method] = f - return f - - return decorate - - -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): - """Call the given command(s).""" - assert isinstance(commands, list) - process = None - - popen_kwargs = {} - if sys.platform == "win32": - # This hides the console window if pythonw.exe is used - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - popen_kwargs["startupinfo"] = startupinfo - - for command in commands: - try: - dispcmd = str([command] + args) - # remember shell=False, so use git.cmd on windows, not just git - process = subprocess.Popen( - [command] + args, - cwd=cwd, - env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr else None), - **popen_kwargs, - ) - break - except OSError: - e = sys.exc_info()[1] - if e.errno == errno.ENOENT: - continue - if verbose: - print("unable to run %s" % dispcmd) - print(e) - return None, None - else: - if verbose: - print("unable to find command, tried %s" % (commands,)) - return None, None - stdout = process.communicate()[0].strip().decode() - if process.returncode != 0: - if verbose: - print("unable to run %s (error)" % dispcmd) - print("stdout was %s" % stdout) - return None, process.returncode - return stdout, process.returncode - - -def versions_from_parentdir(parentdir_prefix, root, verbose): - """Try to determine the version from the parent directory name. - - Source tarballs conventionally unpack into a directory that includes both - the project name and a version string. We will also support searching up - two directory levels for an appropriately named parent directory - """ - rootdirs = [] - - for _ in range(3): - dirname = os.path.basename(root) - if dirname.startswith(parentdir_prefix): - return { - "version": dirname[len(parentdir_prefix) :], - "full-revisionid": None, - "dirty": False, - "error": None, - "date": None, - } - rootdirs.append(root) - root = os.path.dirname(root) # up a level - - if verbose: - print( - "Tried directories %s but none started with prefix %s" - % (str(rootdirs), parentdir_prefix) - ) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs): - """Extract version information from the given file.""" - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords = {} - try: - with open(versionfile_abs, "r") as fobj: - for line in fobj: - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - if line.strip().startswith("git_date ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["date"] = mo.group(1) - except OSError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords(keywords, tag_prefix, verbose): - """Get version information from git keywords.""" - if "refnames" not in keywords: - raise NotThisMethod("Short version file found") - date = keywords.get("date") - if date is not None: - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - - # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant - # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 - # -like" string, which we must then edit to make compliant), because - # it's been around since git-1.5.3, and it's too difficult to - # discover which version we're using, or to work around using an - # older one. - date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = {r.strip() for r in refnames.strip("()").split(",")} - # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of - # just "foo-1.0". If we see a "tag: " prefix, prefer those. - TAG = "tag: " - tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)} - if not tags: - # Either we're using git < 1.8.3, or there really are no tags. We use - # a heuristic: assume all version tags have a digit. The old git %d - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # between branches and tags. By ignoring refnames without digits, we - # filter out many common branch names like "release" and - # "stabilization", as well as "HEAD" and "master". - tags = {r for r in refs if re.search(r"\d", r)} - if verbose: - print("discarding '%s', no digits" % ",".join(refs - tags)) - if verbose: - print("likely tags: %s" % ",".join(sorted(tags))) - for ref in sorted(tags): - # sorting will prefer e.g. "2.0" over "2.0rc1" - if ref.startswith(tag_prefix): - r = ref[len(tag_prefix) :] - # Filter out refs that exactly match prefix or that don't start - # with a number once the prefix is stripped (mostly a concern - # when prefix is '') - if not re.match(r"\d", r): - continue - if verbose: - print("picking %s" % r) - return { - "version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, - "error": None, - "date": date, - } - # no suitable tags, so version is "0+unknown", but full hex is still there - if verbose: - print("no suitable tags, using unknown + full revision id") - return { - "version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, - "error": "no suitable tags", - "date": None, - } - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): - """Get version from 'git describe' in the root of the source tree. - - This only gets called if the git-archive 'subst' keywords were *not* - expanded, and _version.py hasn't already been rewritten with a short - version string, meaning we're inside a checked out source tree. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - - # GIT_DIR can interfere with correct operation of Versioneer. - # It may be intended to be passed to the Versioneer-versioned project, - # but that should not change where we get our version from. - env = os.environ.copy() - env.pop("GIT_DIR", None) - runner = functools.partial(runner, env=env) - - _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) - if rc != 0: - if verbose: - print("Directory %s not under git control" % root) - raise NotThisMethod("'git rev-parse --git-dir' returned error") - - # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] - # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = runner( - GITS, - [ - "describe", - "--tags", - "--dirty", - "--always", - "--long", - "--match", - f"{tag_prefix}[[:digit:]]*", - ], - cwd=root, - ) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root) - # --abbrev-ref was added in git-1.6.3 - if rc != 0 or branch_name is None: - raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") - branch_name = branch_name.strip() - - if branch_name == "HEAD": - # If we aren't exactly on a branch, pick a branch which represents - # the current commit. If all else fails, we are on a branchless - # commit. - branches, rc = runner(GITS, ["branch", "--contains"], cwd=root) - # --contains was added in git-1.5.4 - if rc != 0 or branches is None: - raise NotThisMethod("'git branch --contains' returned error") - branches = branches.split("\n") - - # Remove the first line if we're running detached - if "(" in branches[0]: - branches.pop(0) - - # Strip off the leading "* " from the list of branches. - branches = [branch[2:] for branch in branches] - if "master" in branches: - branch_name = "master" - elif not branches: - branch_name = None - else: - # Pick the first branch that is returned. Good or bad. - branch_name = branches[0] - - pieces["branch"] = branch_name - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if dirty: - git_describe = git_describe[: git_describe.rindex("-dirty")] - - # now we have TAG-NUM-gHEX or HEX - - if "-" in git_describe: - # TAG-NUM-gHEX - mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) - if not mo: - # unparsable. Maybe git-describe is misbehaving? - pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - if verbose: - fmt = "tag '%s' doesn't start with prefix '%s'" - print(fmt % (full_tag, tag_prefix)) - pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( - full_tag, - tag_prefix, - ) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix) :] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root) - pieces["distance"] = len(out.split()) # total number of commits - - # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - - return pieces - - -def plus_or_dot(pieces): - """Return a + if we don't already have one, else return a .""" - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces): - """Build up version string, with post-release "local version identifier". - - Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - Exceptions: - 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_branch(pieces): - """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] . - - The ".dev0" means not master branch. Note that .dev0 sorts backwards - (a feature branch will appear "older" than the master branch). - - Exceptions: - 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0" - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += "+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def pep440_split_post(ver): - """Split pep440 version string at the post-release segment. - - Returns the release segments before the post-release and the - post-release version number (or -1 if no post-release segment is present). - """ - vc = str.split(ver, ".post") - return vc[0], int(vc[1] or 0) if len(vc) == 2 else None - - -def render_pep440_pre(pieces): - """TAG[.postN.devDISTANCE] -- No -dirty. - - Exceptions: - 1: no tags. 0.post0.devDISTANCE - """ - if pieces["closest-tag"]: - if pieces["distance"]: - # update the post release segment - tag_version, post_version = pep440_split_post(pieces["closest-tag"]) - rendered = tag_version - if post_version is not None: - rendered += ".post%d.dev%d" % (post_version + 1, pieces["distance"]) - else: - rendered += ".post0.dev%d" % (pieces["distance"]) - else: - # no commits, use the tag as the version - rendered = pieces["closest-tag"] - else: - # exception #1 - rendered = "0.post0.dev%d" % pieces["distance"] - return rendered - - -def render_pep440_post(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX] . - - The ".dev0" means dirty. Note that .dev0 sorts backwards - (a dirty tree will appear "older" than the corresponding clean one), - but you shouldn't be releasing software with -dirty anyways. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%s" % pieces["short"] - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += "+g%s" % pieces["short"] - return rendered - - -def render_pep440_post_branch(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] . - - The ".dev0" means not master branch. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%s" % pieces["short"] - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += "+g%s" % pieces["short"] - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_old(pieces): - """TAG[.postDISTANCE[.dev0]] . - - The ".dev0" means dirty. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces): - """TAG[-DISTANCE-gHEX][-dirty]. - - Like 'git describe --tags --dirty --always'. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces): - """TAG-DISTANCE-gHEX[-dirty]. - - Like 'git describe --tags --dirty --always -long'. - The distance/hash is unconditional. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render(pieces, style): - """Render the given version pieces into the requested style.""" - if pieces["error"]: - return { - "version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None, - } - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-branch": - rendered = render_pep440_branch(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-post-branch": - rendered = render_pep440_post_branch(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError("unknown style '%s'" % style) - - return { - "version": rendered, - "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], - "error": None, - "date": pieces.get("date"), - } - - -def get_versions(): - """Get version information or return default if unable to do so.""" - # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have - # __file__, we can work backwards from there to the root. Some - # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which - # case we can only use expanded keywords. - - cfg = get_config() - verbose = cfg.verbose - - try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) - except NotThisMethod: - pass - - try: - root = os.path.realpath(__file__) - # versionfile_source is the relative path from the top of the source - # tree (where the .git directory might live) to this file. Invert - # this to find the root from __file__. - for _ in cfg.versionfile_source.split("/"): - root = os.path.dirname(root) - except NameError: - return { - "version": "0+unknown", - "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None, - } - - try: - pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) - return render(pieces, cfg.style) - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - except NotThisMethod: - pass - - return { - "version": "0+unknown", - "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", - "date": None, - } diff --git a/autoflow/autoflow/app.py b/autoflow/autoflow/app.py deleted file mode 100644 index a413fd33a1..0000000000 --- a/autoflow/autoflow/app.py +++ /dev/null @@ -1,78 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Defines the 'main' function, whcih runs when __main__.py is executed. -""" - -import logging -import os -import time -from pathlib import Path - -from get_secret_or_env_var import getenv - -from autoflow.model import init_db -from autoflow.parser import parse_workflows_yaml -from autoflow.sensor import available_dates_sensor - - -def main(run_on_schedule: bool = True): - """ - Main function. Creates output directories, initialises the database, parses - a workflows definition file to define workflows and configure the available - dates sensor, and runs the available dates sensor. - - Parameters - ---------- - run_on_schedule : bool, default True - Set run_on_schedule=False to run the sensor only once, ignoring the schedule. - (useful for testing) - """ - # Initialise logger - # TODO: Use structlog (not sure whether it will be possible for the prefect logger) - log_level = os.environ["AUTOFLOW_LOG_LEVEL"] - logger = logging.getLogger(__name__) - handler = logging.StreamHandler() - formatter = logging.Formatter( - "[%(asctime)s] %(levelname)s - %(name)s | %(message)s" - ) # Match prefect format for now - formatter.converter = time.gmtime - handler.setFormatter(formatter) - logger.addHandler(handler) - logger.setLevel(log_level) - logger.info(f"Log level for logger '{__name__}' set to '{log_level}'.") - - # Make output directories - outputs_path = Path(os.environ["AUTOFLOW_OUTPUTS_DIR"]) - logger.info( - f"Creating output directories '{outputs_path/'notebooks'}' and '{outputs_path/'reports'}'." - ) - (outputs_path / "notebooks").mkdir(exist_ok=True) - (outputs_path / "reports").mkdir(exist_ok=True) - - # Init DB - # Note: AUTOFLOW_DB_URI must be an env var so that it can be used in prefect.config, so we read it using os.environ. - # AUTOFLOW_DB_PASSWORD can (and should) be a docker secret, so we read it using get_secret_or_env_var. - db_uri = os.environ["AUTOFLOW_DB_URI"] - logger.info(f"Initialising database '{db_uri}'.") - init_db(db_uri.format(getenv("AUTOFLOW_DB_PASSWORD", ""))) - - # Create workflows according to workflow definition file - inputs_dir = os.environ["AUTOFLOW_INPUTS_DIR"] - workflow_storage_dir = str(outputs_path / ".prefect/flows") - logger.info(f"Creating workflows defined in '{Path(inputs_dir)/'workflows.yml'}'.") - workflow_storage, sensor_config = parse_workflows_yaml( - "workflows.yml", inputs_dir, workflow_storage_dir - ) - - # Run available dates sensor - logger.info("Running available dates sensor.") - available_dates_sensor.schedule = sensor_config["schedule"] - available_dates_sensor.run( - workflow_configs=sensor_config["workflows"], - cdr_types=sensor_config["cdr_types"], - workflow_storage=workflow_storage, - run_on_schedule=run_on_schedule, - ) diff --git a/autoflow/autoflow/date_stencil.py b/autoflow/autoflow/date_stencil.py deleted file mode 100644 index 2fed4aa558..0000000000 --- a/autoflow/autoflow/date_stencil.py +++ /dev/null @@ -1,213 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Defines the DateStencil class, to represent a pattern of dates relative to a reference date. -""" - -import datetime -from typing import Iterable, List, Optional, Sequence, Set, Tuple, Union - -import pendulum - - -class InvalidDateIntervalError(ValueError): - """ - Custom error to raise if a date stencil contains a date interval with start_date > end_date. - """ - - pass - - -class DateStencil: - """ - A class that represents a sequence of date intervals, which can be a - mixture of absolute dates and offsets relative to a reference date. - - Parameters - ---------- - raw_stencil : sequence of date, int and/or pairs of date/int - List of elements defining date intervals. - Each element can be: - - a date object corresponding to an absolute date, - - an int corresponding to an offset (in days) relative to a reference date, - - a length-2 sequence [start, end] of dates or offsets, - corresponding to a half-open date interval (inclusive of lower limit). - """ - - def __init__( - self, - raw_stencil: Sequence[ - Union[Union[int, datetime.date], Sequence[Union[int, datetime.date]]] - ], - ): - intervals = [] - for element in raw_stencil: - if isinstance(element, (list, tuple)): - if len(element) != 2: - raise ValueError( - "Expected date interval to have length 2 (in format [start, end]), " - f"but got sequence of length {len(element)}." - ) - self._validate_date_element(element[0]) - self._validate_date_element(element[1]) - if ( - (isinstance(element[0], int) and isinstance(element[1], int)) - or ( - isinstance(element[0], datetime.date) - and isinstance(element[1], datetime.date) - ) - ) and element[1] <= element[0]: - raise InvalidDateIntervalError( - f"Date stencil contains invalid interval ({element[0]}, {element[1]})." - ) - intervals.append((element[0], element[1])) - else: - self._validate_date_element(element) - intervals.append((element, self._next_day(element))) - self._intervals = tuple(intervals) - - @staticmethod - def _validate_date_element(element): - if not isinstance(element, (int, datetime.date)): - raise TypeError(f"{element} is not an integer or date.") - - @staticmethod - def _next_day(element: Union[int, datetime.date]) -> Union[int, datetime.date]: - """ - Given a date element (absolute date or integer offset), increment it by 1 day. - """ - if isinstance(element, datetime.date): - return element + pendulum.duration(days=1) - else: - return element + 1 - - @staticmethod - def _offset_to_date( - offset: Union[int, datetime.date], reference_date: datetime.date - ) -> pendulum.Date: - """ - Return a date corresponding to the offset from a reference date. - - Parameters - ---------- - offset : int or date - Either an integer number of days offset from reference date, or a date object. - If a date object, this date will be returned. - reference_date : date - Date to calculate the offset relative to. - - Returns - ------- - pendulum.Date - reference_date + offset (if offset is an integer), or offset (if offset is a date). - - Raises - ------ - TypeError - If type(offset) is not either int or datetime.date - """ - if isinstance(offset, datetime.date): - date_from_offset = pendulum.date(offset.year, offset.month, offset.day) - elif isinstance(offset, int): - date_from_offset = pendulum.date( - reference_date.year, reference_date.month, reference_date.day - ).add(days=offset) - else: - raise TypeError( - f"Invalid type for offset: expected 'date' or 'int', not '{type(offset).__name__}'." - ) - return date_from_offset - - def __eq__(self, other): - if not isinstance(other, DateStencil): - return NotImplemented - return self._intervals == other._intervals - - def as_date_pairs( - self, reference_date: datetime.date - ) -> List[Tuple[pendulum.Date, pendulum.Date]]: - """ - Given a reference date to calculate the offsets relative to, return - this date stencil as a list of tuples representing half-open date intervals - (inclusive of lower limit). - - Parameters - ---------- - reference_date : date - Date to calculate offsets relative to. - - Returns - ------- - list of tuple (pendulum.Date, pendulum.Date) - List of pairs of date objects, each representing a date interval. - - Raises - ------ - InvalidDateIntervalError - If the stencil results in a date pair with end_date <= start_date - """ - date_pairs = [] - for element in self._intervals: - start_date = self._offset_to_date(element[0], reference_date) - end_date = self._offset_to_date(element[1], reference_date) - if end_date <= start_date: - raise InvalidDateIntervalError( - f"Date stencil contains invalid date pair ({start_date}, {end_date}) for reference date {reference_date}." - ) - date_pairs.append((start_date, end_date)) - return date_pairs - - def as_set_of_dates(self, reference_date: datetime.date) -> Set[pendulum.Date]: - """ - Given a reference date to calculate the offsets relative to, return - this date stencil as a set of dates. - - Parameters - ---------- - reference_date : date - Date to calculate offsets relative to. - - Returns - ------- - set of pendulum.Date - Set of dates represented by the stencil - """ - date_pairs = self.as_date_pairs(reference_date=reference_date) - # Have to subtract a day from pair[1] here because pendulum.Period.range returns a range inclusive of both limits - dates = set().union( - *[pendulum.period(pair[0], pair[1].subtract(days=1)) for pair in date_pairs] - ) - return dates - - def dates_are_available( - self, reference_date: datetime.date, available_dates: Iterable[datetime.date] - ) -> bool: - """ - Check whether all dates represented by this date stencil for a - particular reference date are included in a set of available dates. - - Parameters - ---------- - reference_date : date - Date to calculate offsets relative to. - available_dates : iterable of date - Set of available dates - - Returns - ------- - bool - True if all dates are available, False otherwise. - - Notes - ----- - - If the stencil is not valid for the given reference date (i.e. contains - invalid date pairs), this function will return False. - """ - try: - set_of_dates = self.as_set_of_dates(reference_date=reference_date) - except InvalidDateIntervalError: - return False - return set_of_dates.issubset(set(available_dates)) diff --git a/autoflow/autoflow/model.py b/autoflow/autoflow/model.py deleted file mode 100644 index a7a95f7c23..0000000000 --- a/autoflow/autoflow/model.py +++ /dev/null @@ -1,147 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Defines the 'WorkflowRuns' class, which represents the 'workflow_runs' database table for storing the state of workflow runs. -""" - -import os -import enum -import json -from typing import Dict, Any, Optional, Union - -import pendulum -from sqlalchemy import ( - Column, - String, - DateTime, - Date, - Integer, - Enum, - JSON, - create_engine, -) -from sqlalchemy.ext.declarative import declarative_base - -from autoflow.utils import get_params_hash - -Base = declarative_base() - - -class RunState(enum.Enum): - """ - possible states a workflow run can be in. - """ - - running = 1 - success = 2 - failed = 3 - - -# TODO: Add a table for looking up workflow parameters from the parameters hash - - -class WorkflowRuns(Base): - """ - DB Model for storing the state of workflow runs. - """ - - __tablename__ = "workflow_runs" - - id = Column(Integer, primary_key=True) - workflow_name = Column(String) - parameters_hash = Column(String) - state = Column(Enum(RunState)) - timestamp = Column(DateTime(timezone=True)) - - def __init__(self, workflow_name: str, parameters_hash: str, state: RunState): - self.workflow_name = workflow_name - self.parameters_hash = ( - parameters_hash # Hash generated from workflow parameters dict - ) - self.state = state # Flow run state - self.timestamp = pendulum.now("utc") - - @classmethod - def set_state( - cls, - workflow_name: str, - parameters: Dict[str, Any], - state: RunState, - session: "sqlalchemy.orm.session.Session", - ) -> None: - """ - Add a new row to the workflow runs table. - - Parameters - ---------- - workflow_name : str - Name of the workflow - parameters : dict - Parameters passed when running the workflow - state : RunState - The state of the workflow run - session : Session - A sqlalchemy session for a DB in which this model exists. - """ - parameters_hash = get_params_hash(parameters) - row = cls(workflow_name, parameters_hash, state) - session.add(row) - session.flush() - - @classmethod - def get_most_recent_state( - cls, - workflow_name: str, - parameters: Dict[str, Any], - session: "sqlalchemy.orm.session.Session", - ) -> Optional[RunState]: - """ - Get the most recent state for a given (workflow_name, parameters) combination. - - Parameters - ---------- - workflow_name : str - Name of the workflow - parameters : dict - Parameters passed when running the workflow - session : Session - A sqlalchemy session for a DB in which this model exists. - - Returns - ------- - RunState or None - Most recent state, or None if no state has been set. - """ - parameters_hash = get_params_hash(parameters) - most_recent_row = ( - session.query(cls) - .filter( - cls.workflow_name == workflow_name, - cls.parameters_hash == parameters_hash, - ) - .order_by(cls.timestamp.desc()) - .first() - ) - if most_recent_row is not None: - return most_recent_row.state - else: - return None - - -def init_db(db_uri: str, force: bool = False) -> None: - """ - Initialise the database, optionally wipe any existing one first. - - Parameters - ---------- - db_uri : str - Database URI - force : bool - If set to true, wipes any existing database. - """ - engine = create_engine(db_uri) - if force: - Base.metadata.drop_all(engine) - Base.metadata.create_all(engine) diff --git a/autoflow/autoflow/parser/__init__.py b/autoflow/autoflow/parser/__init__.py deleted file mode 100644 index fa9add33b2..0000000000 --- a/autoflow/autoflow/parser/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -autoflow.parser contains definitions for the 'parse_workflows_yaml' function and marshmallow schemas -for parsing a 'workflows.yml' file to define workflows and configure the available dates sensor. -""" - -from .parser import parse_workflows_yaml diff --git a/autoflow/autoflow/parser/available_dates_sensor_schema.py b/autoflow/autoflow/parser/available_dates_sensor_schema.py deleted file mode 100644 index d20fbdab1d..0000000000 --- a/autoflow/autoflow/parser/available_dates_sensor_schema.py +++ /dev/null @@ -1,40 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Defines the AvailableDatesSensorSchema class, for loading configuration parameters for the available dates sensor. -""" - -from marshmallow import ( - fields, - Schema, - validate, -) - -from autoflow.parser.custom_fields import ScheduleField -from autoflow.parser.workflow_config_schema import WorkflowConfigSchema - - -class AvailableDatesSensorSchema(Schema): - """ - Schema for configuration parameters for the available dates sensor. - - Fields - ------ - schedule : str or None - Cron string describing the schedule on which the sensor should check - available dates, or None for no schedule. - cdr_types : list of str, optional - Subset of CDR types for which available dates should be found. - If not provided, defaults to None. - workflows : list of Nested(WorkflowConfigSchema) - List of workflows (and associated parameters) that the sensor should run. - """ - - schedule = ScheduleField(required=True, allow_none=True) - cdr_types = fields.List( - fields.String(validate=validate.OneOf(["calls", "sms", "mds", "topups"])), - missing=None, - ) - workflows = fields.List(fields.Nested(WorkflowConfigSchema), required=True) diff --git a/autoflow/autoflow/parser/custom_fields.py b/autoflow/autoflow/parser/custom_fields.py deleted file mode 100644 index 5fb41c7751..0000000000 --- a/autoflow/autoflow/parser/custom_fields.py +++ /dev/null @@ -1,95 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Custom marshmallow fields used for parsing a 'workflows.yml' file. -""" - -import datetime - -from marshmallow import ( - fields, - ValidationError, -) -from prefect.schedules import CronSchedule - -from autoflow.date_stencil import DateStencil, InvalidDateIntervalError - - -class ScheduleField(fields.String): - """ - Custom field to deserialise a cron string as a prefect CronSchedule. - """ - - def _deserialize( - self, value, attr, data, **kwargs - ) -> "prefect.schedules.schedules.Schedule": - """ - Deserialise a cron string as a cron schedule. - - Returns - ------- - Schedule - Prefect CronSchedule to run a flow according to the schedule - defined by the input string. - - Raises - ------ - ValidationError - if the input value is not a valid cron string or None - """ - cron_string = super()._deserialize(value, attr, data, **kwargs) - try: - schedule = CronSchedule(cron_string) - except ValueError: - raise ValidationError(f"Invalid cron string: '{cron_string}'.") - return schedule - - -class DateField(fields.Date): - """ - Custom field to deserialise a date, which will leave the input value unchanged - if it is already a date object. - """ - - # yaml.safe_load converts iso-format dates to date objects, so we need a - # field that can 'deserialise' dates that are already dates. - def _deserialize(self, value, attr, data, **kwargs) -> datetime.date: - """ - If the input value is a date object, return it unchanged. - Otherwise use marshmallow.fields.Date to deserialise. - """ - if isinstance(value, datetime.date): - return value - else: - return super()._deserialize(value, attr, data, **kwargs) - - -class DateStencilField(fields.Field): - """ - Custom field to deserialise a 'raw' date stencil (i.e. a list of dates, offsets and/or pairs) - to a DateStencil object. - """ - - def _deserialize(self, value, attr, data, **kwargs) -> DateStencil: - """ - Create a DateStencil object from the input value. - - Returns - ------- - DateStencil - - Raises - ------ - ValidationError - if the input value is not a valid 'raw' stencil - - See also - -------- - autoflow.date_stencil.DateStencil - """ - try: - return DateStencil(raw_stencil=value) - except (TypeError, ValueError, InvalidDateIntervalError) as e: - raise ValidationError(str(e)) diff --git a/autoflow/autoflow/parser/notebook_output_schema.py b/autoflow/autoflow/parser/notebook_output_schema.py deleted file mode 100644 index d98eac0beb..0000000000 --- a/autoflow/autoflow/parser/notebook_output_schema.py +++ /dev/null @@ -1,52 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Defines the NotebookOutputSchema class, for loading the 'output' parameter of a -notebook task specification. -""" - -from pathlib import Path - -from marshmallow import ( - fields, - Schema, - validate, - validates, - ValidationError, -) - - -class NotebookOutputSchema(Schema): - """ - Schema for the 'output' parameter of a notebook task specification. - - Fields - ------ - format : str - Format to which notebook should be converted (only 'pdf' is currently supported). - template : str, optional - Filename of a template file to be used when converting the notebook to ASCIIDoc format. - If not provided, deserialises to None. - """ - - format = fields.String(validate=validate.OneOf(["pdf"]), required=True) - template = fields.String(missing=None) - - @validates("template") - def validate_template(self, value): - """ - Raise a ValidationError if the specified template file does not exist, - or if the inputs directory is not specified in the context. - """ - if value is None: - return - try: - inputs_dir = Path(self.context["inputs_dir"]) - except KeyError: - raise ValidationError( - "'inputs_dir' was not provided in the context. Cannot check for file existence." - ) - if not (inputs_dir / value).exists(): - raise ValidationError(f"Asciidoc template file '{value}' not found.") diff --git a/autoflow/autoflow/parser/notebook_schema.py b/autoflow/autoflow/parser/notebook_schema.py deleted file mode 100644 index ef6e344f1d..0000000000 --- a/autoflow/autoflow/parser/notebook_schema.py +++ /dev/null @@ -1,57 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Defines the NotebookSchema class, for loading a notebook task specification. -""" - -from pathlib import Path - -from marshmallow import ( - fields, - Schema, - validates, - ValidationError, -) - -from autoflow.parser.notebook_output_schema import NotebookOutputSchema - - -class NotebookSchema(Schema): - """ - Schema for a notebook task specification. - - Fields - ------ - filename : str - Filename of Jupyter notebook to be executed. - parameters : dict, optional - Dictionary mapping parameter names (as they will be accessible within - the notebook) to the names of task outputs within the surrounding workflow. - output : Nested(NotebookOutputSchema), optional - Configuration parameters for the output generation task. - """ - - filename = fields.String(required=True) - parameters = fields.Dict( - keys=fields.String(), values=fields.String(), required=False - ) - output = fields.Nested(NotebookOutputSchema, required=False) - - @validates("filename") - def validate_filename(self, value): - """ - Raise a ValidationError if the specified file does not exist or is not a Jupyter notebook, - or if the inputs directory is not specified in the context. - """ - if Path(value).suffix != ".ipynb": - raise ValidationError(f"File '{value}' is not a Jupyter notebook.") - try: - inputs_dir = Path(self.context["inputs_dir"]) - except KeyError: - raise ValidationError( - "'inputs_dir' was not provided in the context. Cannot check for file existence." - ) - if not (inputs_dir / value).exists(): - raise ValidationError(f"Notebook '{value}' not found.") diff --git a/autoflow/autoflow/parser/notebooks_field.py b/autoflow/autoflow/parser/notebooks_field.py deleted file mode 100644 index 9547f9249c..0000000000 --- a/autoflow/autoflow/parser/notebooks_field.py +++ /dev/null @@ -1,68 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Defines the NotebooksField class, to deserialise a collection of notebook task -specifications as an OrderedDict. -""" - -from typing import Any, Dict, OrderedDict - -from marshmallow import ( - fields, - validate, - ValidationError, -) - -from autoflow.parser.notebook_schema import NotebookSchema -from autoflow.utils import sort_notebooks - - -class NotebooksField(fields.Dict): - """ - Custom field to validate a collection of notebook task specifications, - and return them in a topologically-sorted order. - """ - - def __init__(self, **kwargs): - # Ensure that the 'keys' and 'values' arguments cannot be specified. - if "keys" in kwargs: - raise TypeError("The Notebooks field does not accept a 'keys' argument.") - if "values" in kwargs: - raise TypeError("The Notebooks field does not accept a 'values' argument.") - super().__init__( - keys=fields.String( - validate=validate.NoneOf( - ["reference_date", "date_ranges", "flowapi_url"] - ) - ), - values=fields.Nested(NotebookSchema), - **kwargs, - ) - - def _deserialize( - self, value, attr, data, **kwargs - ) -> OrderedDict[str, Dict[str, Any]]: - """ - Deserialise notebook task specifications as an OrderedDict. - - Returns - ------- - OrderedDict - Notebook task specifications, ordered so that no notebook depends - on another notebook that comes after it. - - Raises - ------ - ValidationError - if the notebook task specifications contain circular dependencies. - """ - notebooks = super()._deserialize(value, attr, data, **kwargs) - try: - sorted_notebooks = sort_notebooks(notebooks) - except ValueError: - raise ValidationError( - "Notebook specifications contain circular dependencies." - ) - return sorted_notebooks diff --git a/autoflow/autoflow/parser/parser.py b/autoflow/autoflow/parser/parser.py deleted file mode 100644 index ac707a53e3..0000000000 --- a/autoflow/autoflow/parser/parser.py +++ /dev/null @@ -1,78 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Defines the 'parse_workflows_yaml' function, and marshmallow schemas for parsing -a 'workflows.yml' file to define workflows and configure the available dates sensor. -""" - -from pathlib import Path -from typing import Dict, List, Tuple, Union - -import yaml - -from autoflow.parser.available_dates_sensor_schema import AvailableDatesSensorSchema -from autoflow.parser.workflow_schema import WorkflowSchema - - -def parse_workflows_yaml( - filename: str, inputs_dir: str, workflow_storage_dir: str -) -> Tuple[ - "prefect.storage.Storage", - Dict[ - str, - Union[ - "prefect.schedules.Schedule", - List[str], - List["autoflow.sensor.WorkflowConfig"], - ], - ], -]: - """ - Construct workflows defined in an input file. - - Parameters - ---------- - filename : str - Name of yaml input file - inputs_dir : str - Directory in which input files should be found - workflow_storage_dir : str - Directory in which workflow objects will be stored (using prefect Local storage) - - Returns - ------- - workflow_storage : prefect.storage.Storage - Prefect Storage object containing the workflows - sensor_config : dict - Dict of sensor config options: - - 'schedule': prefect Schedule on which the sensor will run - - 'cdr_types': list of CDR types for which available dates will be found - - 'workflows': list of parameterised workflows to be run - """ - with open(Path(inputs_dir) / filename, "r") as f: - workflows_yaml = yaml.safe_load(f) - - try: - workflows_spec = workflows_yaml["workflows"] - except KeyError: - raise ValueError("Input file does not have a 'workflows' section.") - try: - sensor_spec = workflows_yaml["available_dates_sensor"] - except KeyError: - raise ValueError( - "Input file does not have an 'available_dates_sensor' section." - ) - - workflow_schema = WorkflowSchema( - context=dict(inputs_dir=inputs_dir, workflow_storage_dir=workflow_storage_dir) - ) - workflow_storage = workflow_schema.load(workflows_spec, many=True) - - sensor_schema = AvailableDatesSensorSchema( - context=dict(workflow_storage=workflow_storage) - ) - sensor_config = sensor_schema.load(sensor_spec) - - return workflow_storage, sensor_config diff --git a/autoflow/autoflow/parser/workflow_config_schema.py b/autoflow/autoflow/parser/workflow_config_schema.py deleted file mode 100644 index 61bb107477..0000000000 --- a/autoflow/autoflow/parser/workflow_config_schema.py +++ /dev/null @@ -1,119 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Defines the WorkflowConfigSchema class, for loading parameters that the sensor will use to run a workflow. -""" - -from marshmallow import ( - fields, - Schema, - post_load, - validate, - validates, - validates_schema, - ValidationError, -) - -from autoflow.parser.custom_fields import DateField, DateStencilField -from autoflow.sensor import WorkflowConfig - - -class WorkflowConfigSchema(Schema): - """ - Schema for parameters that the available dates sensor will use to run a workflow. - - Fields - ------ - workflow_name : str - Name of the workflow to run. - parameters : dict, optional - Parameters with which the workflow will run. - earliest_date : date, optional - Earliest date of CDR data for which the workflow should run. - date_stencil : list of int, date and/or pairs of int/date, optional - Date stencil describing a pattern of dates that must be available for the workflow to run. - """ - - # Parameter names that will always be passed to the workflow by the available dates sensor. - # TODO: Allow automatic parameter names to be specified at schema initialisation, instead of hard-coded. - _automatic_parameters = ("reference_date", "date_ranges") - # Fields - workflow_name = fields.String(required=True) - parameters = fields.Dict( - keys=fields.String(validate=validate.NoneOf(_automatic_parameters)), - required=False, - ) - earliest_date = DateField(required=False) - date_stencil = DateStencilField(required=False) - - @validates("workflow_name") - def validate_workflow(self, value): - """ - Raise a ValidationError if the named workflow does not exist, or - doesn't accept parameters 'reference_date' and 'date_ranges'. - """ - # Check that workflow exists - try: - if value not in self.context["workflow_storage"]: - raise ValidationError("Workflow does not exist in this storage.") - except KeyError: - raise ValidationError( - "'workflow_storage' was not provided in the context. Cannot check for workflow existence." - ) - # Check that workflow accepts parameters that will automatically be passed when it runs - workflow_parameter_names = { - p.name - for p in self.context["workflow_storage"].get_flow(value).parameters() - } - missing_automatic_parameters = set(self._automatic_parameters).difference( - workflow_parameter_names - ) - if missing_automatic_parameters: - raise ValidationError( - f"Workflow does not accept parameters {missing_automatic_parameters}." - ) - - @validates_schema - def validate_workflow_parameters(self, data, **kwargs): - """ - Raise a ValidationError if any required workflow parameters are not - provided, or if any unexpected parameters are provided. - """ - errors = {} - # Parameters workflow expects - workflow_parameters = ( - self.context["workflow_storage"] - .get_flow(data["workflow_name"]) - .parameters() - ) - parameter_names = {p.name for p in workflow_parameters} - required_parameter_names = {p.name for p in workflow_parameters if p.required} - # Parameters workflow will receive - provided_parameter_names = set(data.get("parameters", {}).keys()).union( - self._automatic_parameters - ) - # Required parameters that are not provided - missing_parameters = required_parameter_names.difference( - provided_parameter_names - ) - if missing_parameters: - errors["parameters"] = errors.get("parameters", []) + [ - f"Missing required parameters {missing_parameters} for workflow '{data['workflow_name']}'." - ] - # Extra parameters that the workflow is not expecting - unexpected_parameters = provided_parameter_names.difference(parameter_names) - if unexpected_parameters: - errors["parameters"] = errors.get("parameters", []) + [ - f"Unexpected parameters provided for workflow '{data['workflow_name']}': {unexpected_parameters}." - ] - if errors: - raise ValidationError(errors) - - @post_load - def make_workflow_config(self, data, **kwargs) -> WorkflowConfig: - """ - Return the provided workflow config parameters in a WorkflowConfig namedtuple. - """ - return WorkflowConfig(**data) diff --git a/autoflow/autoflow/parser/workflow_schema.py b/autoflow/autoflow/parser/workflow_schema.py deleted file mode 100644 index 172d33a525..0000000000 --- a/autoflow/autoflow/parser/workflow_schema.py +++ /dev/null @@ -1,69 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Defines the WorkflowSchema class, for loading a notebook-based workflow specification. -""" -from typing import Optional - -from marshmallow import ( - fields, - Schema, - post_load, - validates_schema, - ValidationError, -) -from prefect import storage - -from autoflow.parser.notebooks_field import NotebooksField -from autoflow.workflows import make_notebooks_workflow - - -class WorkflowSchema(Schema): - """ - Schema for a notebook-based workflow specification. - - Fields - ------ - name : str - Name of the prefect flow. - notebooks : dict - Dictionary of notebook task specifications. - """ - - name = fields.String(required=True) - notebooks = NotebooksField(required=True) - - @validates_schema(pass_many=True) - def check_for_duplicate_names(self, data, many, **kwargs): - """ - If this schema is used with 'many=True', raise a ValidationError if any workflow names are duplicated. - """ - if many: - errors = {} - names = set() - for i, workflow in enumerate(data): - if workflow["name"] in names: - errors[i] = {"name": [f"Duplicate workflow name."]} - else: - names.add(workflow["name"]) - if errors: - raise ValidationError(errors) - else: - pass - - @post_load(pass_many=True) - def make_and_store_workflows(self, data, many, **kwargs) -> storage.Local: - """ - Create a prefect flow for each of the provided workflow specifications, - and return as a prefect 'Local' storage object. - """ - - if not many: - data = [data] - workflow_storage = storage.Local(directory=self.context["workflow_storage_dir"]) - for workflow_spec in data: - workflow = make_notebooks_workflow(**workflow_spec) - workflow_storage.add_flow(workflow) - return workflow_storage diff --git a/autoflow/autoflow/sensor.py b/autoflow/autoflow/sensor.py deleted file mode 100644 index 15b9249ae9..0000000000 --- a/autoflow/autoflow/sensor.py +++ /dev/null @@ -1,345 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Defines 'available_dates_sensor' prefect flow. -""" - -import warnings -from typing import Any, Dict, List, NamedTuple, NoReturn, Optional, Sequence, Tuple - -import pendulum -import prefect -from get_secret_or_env_var import environ, getenv -from prefect import Flow, Parameter, task, unmapped -from prefect.engine import signals -from prefect.schedules import CronSchedule -from prefect.triggers import all_successful, any_failed - -import flowclient - -from autoflow.date_stencil import DateStencil -from autoflow.model import RunState, WorkflowRuns -from autoflow.utils import session_scope - - -class WorkflowConfig(NamedTuple): - """ - A namedtuple containing a workflow and its parameters. - - Attributes - ---------- - workflow_name : str - Name of a prefect flow. - parameters : dict - Dict of parameters with which the workflow should be run. - earliest_date : date - Earliest date for which the workflow should run. - date_stencil : DateStencil - Date stencil defining date intervals required by the workflow. - The default is DateStencil([0]) (i.e. a stencil that contains only the reference date). - """ - - workflow_name: str - parameters: Optional[Dict[str, Any]] = None - earliest_date: Optional["datetime.date"] = None - date_stencil: DateStencil = DateStencil([0]) - - -# Tasks ----------------------------------------------------------------------- - - -@task -def get_available_dates( - cdr_types: Optional[Sequence[str]] = None, -) -> List[pendulum.Date]: - """ - Task to return a union of the dates for which data is available in FlowDB for the specified set of CDR types. - - Parameters - ---------- - cdr_types : list of str, optional - Subset of CDR types for which to find available dates. - If not provided, the union of available dates for all CDR types will be returned. - - Returns - ------- - list of pendulum.Date - List of available dates, in chronological order - """ - prefect.context.logger.info( - f"Getting available dates from FlowAPI at '{prefect.config.flowapi_url}'." - ) - conn = flowclient.connect( - url=prefect.config.flowapi_url, - token=environ["FLOWAPI_TOKEN"], - ssl_certificate=getenv("SSL_CERTIFICATE_FILE"), - ) - dates = flowclient.get_available_dates(connection=conn) - prefect.context.logger.debug(f"Available dates: {dates}") - if cdr_types is None: - prefect.context.logger.debug( - "No CDR types provided. Will return available dates for all CDR types." - ) - cdr_types = dates.keys() - else: - prefect.context.logger.debug( - f"Returning available dates for CDR types {cdr_types}." - ) - unknown_cdr_types = set(cdr_types).difference(dates.keys()) - if unknown_cdr_types: - warnings.warn(f"No data available for CDR types {unknown_cdr_types}.") - dates_union = set.union( - *[ - set(pendulum.parse(date, exact=True) for date in dates[cdr_type]) - for cdr_type in cdr_types - if cdr_type in dates.keys() - ] - ) - return sorted(list(dates_union)) - - -@task -def filter_dates( - available_dates: Sequence["datetime.date"], workflow_config: WorkflowConfig -) -> List["datetime.date"]: - """ - Filter task to return only dates later than or equal to - workflow_config.earliest_date, for which all dates represented by - workflow_config.date_stencil are available. - If workflow_config.earliest_date is None, date stencil availability will be - checked for all available dates. - - Parameters - ---------- - available_dates : list of date - List of dates to filter - workflow_config : WorkflowConfig - Workflow config with attributes 'earliest_date' and 'date_stencil' - - Returns - ------- - list of date - Filtered list of dates - """ - prefect.context.logger.info(f"Filtering list of available dates.") - filtered_dates = list(available_dates) - - if workflow_config.earliest_date is None: - prefect.context.logger.debug("No earliest date provided.") - else: - prefect.context.logger.debug( - f"Filtering out dates earlier than {workflow_config.earliest_date}." - ) - filtered_dates = [ - date for date in filtered_dates if date >= workflow_config.earliest_date - ] - - prefect.context.logger.debug( - f"Returning reference dates for which all dates in stencil are available." - ) - filtered_dates = [ - date - for date in filtered_dates - if workflow_config.date_stencil.dates_are_available(date, available_dates) - ] - - return filtered_dates - - -@task -def get_parametrised_workflows( - workflow_configs: List[WorkflowConfig], - lists_of_dates: List[List["datetime.date"]], - workflow_storage: "prefect.storage.Storage", -) -> List[Tuple[Flow, Dict[str, Any]]]: - """ - For each workflow in a list of workflow configs, for each date in the - corresponding list of dates, return a tuple (workflow, parameters) with - parameters 'reference_date' and 'date_ranges' added to the parameters dict. - - Parameters - ---------- - workflow_configs : list of WorkflowConfig - List of workflow configs. - lists_of_dates : list of list of date - List containing a list of dates for each workflow in workflow_configs. - workflow_storage : prefect.storage.Storage - Prefect Storage object containing the workflows named in workflow_configs. - - Returns - ------- - list of tuple (Flow, dict) - List of tuples, each containing a prefect Flow and the parameters with which it should be run. - """ - # Note: This is a 'reduce' task (i.e. it is not mapped over workflow configs). - # This is because otherwise, tasks downstream of this one would need to be double-mapped - # (once over workflow configs, and then over dates), and Prefect doesn't currently support nested mapping. - prefect.context.logger.info( - "Adding parameters 'reference_date' and 'date_ranges' to workflow parameters." - ) - prefect.context.logger.debug(f"Workflow configs: {workflow_configs}") - prefect.context.logger.debug(f"Dates: {lists_of_dates}") - return [ - ( - workflow_storage.get_flow(workflow_config.workflow_name), - dict( - workflow_config.parameters or {}, - reference_date=date, - date_ranges=workflow_config.date_stencil.as_date_pairs( - reference_date=date - ), - ), - ) - for workflow_config, dates in zip(workflow_configs, lists_of_dates) - for date in dates - ] - - -@task -def skip_if_already_run(parametrised_workflow: Tuple[Flow, Dict[str, Any]]) -> None: - """ - Task to raise a SKIP signal if a workflow is already running or has previously run successfully - with the given parameters. - - Parameters - ---------- - parametrised_workflow : tuple (prefect.Flow, dict) - Workflow, and associated parameters, for which previous runs should be checked - - Raises - ------ - prefect.engine.signals.SKIP - if this workflow with these parameters has already run successfully - """ - workflow, parameters = parametrised_workflow - prefect.context.logger.info( - f"Checking whether workflow '{workflow.name}' has already run successfully with parameters {parameters}." - ) - with session_scope(prefect.config.db_uri) as session: - state = WorkflowRuns.get_most_recent_state( - workflow_name=workflow.name, parameters=parameters, session=session - ) - - if state is None: - prefect.context.logger.debug( - f"Workflow '{workflow.name}' has not previously run with parameters {parameters}." - ) - elif state == RunState.failed: - prefect.context.logger.debug( - f"Workflow '{workflow.name}' previously failed with parameters {parameters}." - ) - elif state == RunState.running: - raise signals.SKIP( - f"Workflow '{workflow.name}' is already running with parameters {parameters}." - ) - elif state == RunState.success: - raise signals.SKIP( - f"Workflow '{workflow.name}' previously ran successfully with parameters {parameters}." - ) - else: - # This should never happen - raise ValueError(f"Unrecognised workflow state: '{state}'.") - - -@task -def record_workflow_run_state( - parametrised_workflow: Tuple[Flow, Dict[str, Any]], state: RunState -) -> None: - """ - Add a row to the database to record the state of a workflow run. - - Parameters - ---------- - parametrised_workflow : tuple (prefect.Flow, dict) - Workflow, and associated parameters, for which to record state. - state : RunState - Workflow run state. - """ - workflow, parameters = parametrised_workflow - prefect.context.logger.debug( - f"Recording workflow '{workflow.name}' with parameters {parameters} as '{state.name}'." - ) - with session_scope(prefect.config.db_uri) as session: - WorkflowRuns.set_state( - workflow_name=workflow.name, - parameters=parameters, - state=state, - session=session, - ) - - -@task -def run_workflow(parametrised_workflow: Tuple[Flow, Dict[str, Any]]) -> None: - """ - Run a workflow. - - Parameters - ---------- - parametrised_workflow : tuple (prefect.Flow, dict) - Workflow to run, and parameters to run it with. - - Notes - ----- - - The workflow will run once, starting immediately. If the workflow has a - schedule, the schedule will be ignored. - """ - workflow, parameters = parametrised_workflow - prefect.context.logger.info( - f"Running workflow '{workflow.name}' with parameters {parameters}." - ) - state = workflow.run(parameters=parameters, run_on_schedule=False) - if state.is_successful(): - prefect.context.logger.info( - f"Workflow '{workflow.name}' ran successfully with parameters {parameters}." - ) - else: - raise signals.FAIL( - f"Workflow '{workflow.name}' failed when run with parameters {parameters}." - ) - - -# Flows ----------------------------------------------------------------------- - - -with Flow(name="Available dates sensor") as available_dates_sensor: - # TODO: Read workflow configs from a db table, so that new workflows could be added while the date sensor is running. - workflow_configs = Parameter("workflow_configs") - available_dates = get_available_dates( - cdr_types=Parameter("cdr_types", required=False) - ) - filtered_dates = filter_dates.map( - available_dates=unmapped(available_dates), workflow_config=workflow_configs - ) - parametrised_workflows = get_parametrised_workflows( - workflow_configs=workflow_configs, - lists_of_dates=filtered_dates, - workflow_storage=Parameter("workflow_storage"), - ) - - running = record_workflow_run_state.map( - parametrised_workflow=parametrised_workflows, - state=unmapped(RunState.running), - upstream_tasks=[ - skip_if_already_run.map(parametrised_workflow=parametrised_workflows) - ], - ) - workflow_runs = run_workflow.map( - parametrised_workflow=parametrised_workflows, upstream_tasks=[running] - ) - success = record_workflow_run_state.map( - parametrised_workflow=parametrised_workflows, - state=unmapped(RunState.success), - upstream_tasks=[workflow_runs], - task_args=dict(trigger=all_successful), - ) - failed = record_workflow_run_state.map( - parametrised_workflow=parametrised_workflows, - state=unmapped(RunState.failed), - upstream_tasks=[workflow_runs], - task_args=dict(trigger=any_failed), - ) - -available_dates_sensor.set_reference_tasks([success]) diff --git a/autoflow/autoflow/utils.py b/autoflow/autoflow/utils.py deleted file mode 100644 index 541fa7225f..0000000000 --- a/autoflow/autoflow/utils.py +++ /dev/null @@ -1,266 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Utility functions. -""" - -import collections -import json -from contextlib import contextmanager -from hashlib import md5 -from pathlib import Path -from tempfile import TemporaryDirectory -from typing import Any, Dict, Iterator, Optional, OrderedDict, Set, Tuple, Union - -import nbconvert -import nbformat -import networkx as nx -import pendulum -from get_secret_or_env_var import getenv -from sh import asciidoctor_pdf -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker - - -def get_output_filename(input_filename: str, tag: str = "") -> str: - """ - Given an input filename, construct an output filename with the same extension, - with a timestamp (and optionally a tag) added to the stem. - - Parameters - ---------- - input_filename : str - Input filename - tag : str, optional - A tag to append to the file stem - - Returns - ------- - str - Output filename - """ - input_filename = Path(input_filename) - now_string = pendulum.now("utc").format("YYYYMMDDTHHmmss[Z]") - tag = f"__{tag}" if tag != "" else tag - return f"{input_filename.stem}{tag}__{now_string}{input_filename.suffix}" - - -def get_params_hash(parameters: Dict[str, Any]) -> str: - """ - Generate a md5 hash string from a dictionary of parameters. - The dictionary is dumped to a json string before hashing. - - Parameters - ---------- - parameters : dict - Dictionary of parameters to use for creating the hash. - - Returns - ------- - str - md5 hash of the parameters dict - """ - return md5( - json.dumps(dict(parameters), sort_keys=True, default=str).encode() - ).hexdigest() - - -def get_session(db_uri: str) -> "sqlalchemy.orm.session.Session": - """ - Create a sqlalchemy session. - - Parameters - ---------- - db_uri : str - Database URI - - Returns - ------- - Session - A sqlalchemy session - """ - # TODO: This seems like the wrong place to be reading a secret / env var, - # but we can't put a docker secret in the prefect config. - full_db_uri = db_uri.format(getenv("AUTOFLOW_DB_PASSWORD", "")) - engine = create_engine(full_db_uri) - return sessionmaker(bind=engine)() - - -@contextmanager -def session_scope(db_uri: str) -> Iterator["sqlalchemy.orm.session.Session"]: - """ - Provide a scoped sqlalchemy session for interacting with the database. - - Parameters - ---------- - db_uri : str - Database URI - - Yields - ------ - Session - A sqlalchemy session - """ - session = get_session(db_uri) - try: - yield session - session.commit() - except: - session.rollback() - raise - finally: - session.close() - - -def make_json_serialisable(obj: Any) -> Union[dict, list, str, int, float, bool, None]: - """ - Helper function to convert an object's type so that it can be serialised using - the default json serialiser. Non-serialisable types are converted to strings. - - Parameters - ---------- - obj : any - Object to be converted. - - Returns - ------- - dict, list, str, int, float, bool or None - The same object after dumping to json (converting to str if necessary) and loading - """ - return json.loads(json.dumps(obj, default=str)) - - -def get_additional_parameter_names_for_notebooks( - notebooks: Dict[str, Dict[str, Any]], - reserved_parameter_names: Optional[Set[str]] = None, -) -> Set[str]: - """ - Extract parameter names from a list of notebook task specifications. - - Parameters - ---------- - notebooks : dict - Dictionary of dictionaries describing notebook tasks. - reserved_parameter_names : set of str, optional - Names of parameters used within workflow. - - Returns - ------- - set of str - Names of parameters used by notebooks which must be passed to a workflow. - """ - # Parameters requested in notebooks - notebook_parameter_names = set.union( - *[ - set(notebook.get("parameters", {}).values()) - for notebook in notebooks.values() - ] - ) - # Parameters available to notebooks, which are not passed externally as flow parameters - internal_parameter_names = (reserved_parameter_names or set()).union( - notebooks.keys() - ) - # Additional parameters required for notebooks - additional_parameter_names_for_notebooks = notebook_parameter_names.difference( - internal_parameter_names - ) - return additional_parameter_names_for_notebooks - - -def sort_notebooks( - notebooks: Dict[str, Dict[str, Any]] -) -> OrderedDict[str, Dict[str, Any]]: - """ - Perform a topological sort on a dictionary of notebook task specifications. - - Parameters - ---------- - notebooks : dict - Dictionary of dictionaries describing notebook tasks. - - Returns - ------- - OrderedDict - Ordered dict of notebook task dicts, ordered so that no notebook depends on another - notebook that comes after it. - - Raises - ------ - ValueError - If the notebook specifications contain circular dependencies. - """ - notebooks_graph = nx.DiGraph( - { - key: [ - value - for value in notebooks[key].get("parameters", {}).values() - if value in notebooks - ] - for key in notebooks - } - ).reverse() - try: - sorted_notebook_keys = list(nx.topological_sort(notebooks_graph)) - except nx.NetworkXUnfeasible: - raise ValueError("Notebook specifications contain circular dependencies.") - - return collections.OrderedDict( - (key, notebooks[key]) for key in sorted_notebook_keys - ) - - -def notebook_to_asciidoc( - notebook_path: str, asciidoc_template_path: Optional[str] = None -) -> Tuple[str, "nbconvert.exporters.exporter.ResourcesDict"]: - """ - Convert a Jupyter notebook to ASCIIDoc, using nbconvert. - - Parameters - ---------- - notebook_path : str - Path to notebook - asciidoc_template_path : str, optional - Path to a template file to use for the conversion. - If not provided, the default nbconvert asciidoc template will be used. - - Returns - ------- - str - The resulting converted notebook. - ResourcesDict - Dictionary of resources used prior to and during the conversion process. - """ - with open(notebook_path) as nb_file: - nb_read = nbformat.read(nb_file, as_version=4) - - if asciidoc_template_path is None: - exporter = nbconvert.ASCIIDocExporter() - else: - exporter = nbconvert.ASCIIDocExporter(template_file=asciidoc_template_path) - body, resources = exporter.from_notebook_node(nb_read) - - return body, resources - - -def asciidoc_to_pdf( - body: str, resources: "nbconvert.exporters.exporter.ResourcesDict", output_path: str -) -> None: - """ - Convert an ASCIIDoc document to PDF, using asciidoctor-pdf. - - Parameters - ---------- - body : str - Content of the ASCIIDoc document - resources : ResourcesDict - Dictionary of resources, as returned from nbconvert exporter - """ - with TemporaryDirectory() as tmpdir: - with open(f"{tmpdir}/tmp.asciidoc", "w") as f_tmp: - f_tmp.write(body) - for fname, content in resources["outputs"].items(): - with open(f"{tmpdir}/{fname}", "wb") as fout: - fout.write(content) - asciidoctor_pdf(f"{tmpdir}/tmp.asciidoc", o=output_path) diff --git a/autoflow/autoflow/workflows.py b/autoflow/autoflow/workflows.py deleted file mode 100644 index 46fc3d8a0e..0000000000 --- a/autoflow/autoflow/workflows.py +++ /dev/null @@ -1,237 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Contains tasks and functions for constructing Prefect workflows. -""" - -from pathlib import Path -from typing import Any, Dict, Optional, OrderedDict - -import papermill -import prefect - -from autoflow.utils import ( - asciidoc_to_pdf, - get_additional_parameter_names_for_notebooks, - get_output_filename, - get_params_hash, - make_json_serialisable, - notebook_to_asciidoc, -) - - -# Tasks ----------------------------------------------------------------------- - - -@prefect.task -def get_flowapi_url() -> str: - """ - Task to return FlowAPI URL. - - Returns - ------- - str - FlowAPI URL - """ - # This task is defined so that the flowapi url can be passed as a parameter - # to notebook tasks. - return prefect.config.flowapi_url - - -@prefect.task -def get_tag(reference_date: Optional["datetime.date"] = None) -> str: - """ - Task to get a string to append to output filenames from this workflow run. - The tag is unique for each set of workflow parameters. - - Parameters - ---------- - reference_date : date, optional - Reference date for which the workflow is running - - Returns - ------- - str - Tag for output filenames - """ - params_hash = get_params_hash(prefect.context.parameters) - ref_date_string = f"_{reference_date}" if reference_date is not None else "" - return f"{prefect.context.flow_name}{ref_date_string}_{params_hash}" - - -@prefect.task -def papermill_execute_notebook( - input_filename: str, - output_tag: str, - parameters: Optional[Dict[str, Any]] = None, - **kwargs, -) -> str: - """ - Task to execute a notebook using Papermill. - - Parameters - ---------- - input_filename : str - Filename of input notebook (assumed to be in the inputs directory) - output_tag : str - Tag to append to output filename - parameters : dict, optional - Parameters to pass to the notebook - **kwargs - Additional keyword arguments to pass to papermill.execute_notebook - - Returns - ------- - str - Path to executed notebook - """ - # Papermill injects all parameters into the notebook metadata, which gets - # json-serialised, so all parameters must be json serialisable - # (see https://github.com/nteract/papermill/issues/412). - # 'make_json_serialisable()' has the convenient side-effect of converting tuples to - # lists, where we would otherwise have to register a custom papermill translator - # for tuples. - safe_params = make_json_serialisable(parameters) - prefect.context.logger.info( - f"Executing notebook '{input_filename}' with parameters {safe_params}." - ) - - output_filename = get_output_filename(input_filename=input_filename, tag=output_tag) - input_path = str(Path(prefect.config.inputs.inputs_dir) / input_filename) - output_path = str(Path(prefect.config.outputs.notebooks_dir) / output_filename) - - prefect.context.logger.debug(f"Output notebook will be '{output_path}'.") - - papermill.execute_notebook( - input_path, output_path, parameters=safe_params, **kwargs - ) - - prefect.context.logger.info(f"Finished executing notebook.") - - return output_path - - -@prefect.task -def convert_notebook_to_pdf( - notebook_path: str, - output_filename: Optional[str] = None, - asciidoc_template: Optional[str] = None, -) -> str: - """ - Task to convert a notebook to PDF, via asciidoc (without executing the notebook). - - Parameters - ---------- - notebook_path : str - Path to notebook - output_filename : str, optional - Filename for output PDF file. - If not provided, this will be the name of the input notebook with the extension changed to '.pdf' - asciidoc_template : str, optional - Filename of a non-default template to use when exporting to asciidoc - (assumed to be in the inputs directory) - - Returns - ------- - str - Path to output PDF file - """ - prefect.context.logger.info(f"Converting notebook '{notebook_path}' to PDF.") - if output_filename is None: - output_filename = f"{Path(notebook_path).stem}.pdf" - output_path = str(Path(prefect.config.outputs.reports_dir) / output_filename) - - if asciidoc_template is None: - if "asciidoc_template_path" in prefect.config: - asciidoc_template_path = prefect.config.asciidoc_template_path - else: - # If no template is provided, and no default template is set in the config, - # run nbconvert without specifying a template (i.e. use the default nbconvert asciidoc template). - asciidoc_template_path = None - else: - asciidoc_template_path = str( - Path(prefect.config.inputs.inputs_dir) / asciidoc_template - ) - prefect.context.logger.debug( - f"Using template '{asciidoc_template_path}' to convert notebook to asciidoc." - ) - - body, resources = notebook_to_asciidoc(notebook_path, asciidoc_template_path) - - prefect.context.logger.debug("Converted notebook to asciidoc.") - prefect.context.logger.debug("Converting asciidoc to PDF...") - - asciidoc_to_pdf(body, resources, output_path) - - prefect.context.logger.info(f"Created report '{output_filename}'.") - - return output_path - - -# Flows ----------------------------------------------------------------------- - - -def make_notebooks_workflow( - name: str, notebooks: OrderedDict[str, Dict[str, Any]] -) -> prefect.Flow: - """ - Build a prefect flow that runs a set of interdependent Jupyter notebooks. - The FlowAPI URL will be available to the notebooks as parameter 'flowapi_url'. - - Parameters - ---------- - name : str - Name for the workflow - notebooks : OrderedDict - Ordered dictionary of dictionaries describing notebook tasks. - Each should have keys 'filename' and 'parameters', and optionally 'output'. - - Returns - ------- - prefect.Flow - Workflow that runs the notebooks. - """ - # Get parameter names from notebooks dict - parameter_names = get_additional_parameter_names_for_notebooks( - notebooks=notebooks, reserved_parameter_names={"flowapi_url"} - ) - - # Define workflow - with prefect.Flow(name=name) as workflow: - # Parameters - parameter_tasks = { - pname: prefect.Parameter(pname) - for pname in parameter_names.union({"reference_date", "date_ranges"}) - } - # Instantiating a Parameter doesn't add it to the flow. The available dates sensor - # will always pass the "date_ranges" parameter, so we need to explicitly add this - # parameter in case it's not used by any of the notebook tasks - # (https://github.com/PrefectHQ/prefect/issues/1541) - workflow.add_task(parameter_tasks["date_ranges"]) - - # Get FlowAPI URL so that it can be passed as a parameter to notebook execution tasks - parameter_tasks["flowapi_url"] = get_flowapi_url() - - # Get unique tag for this workflow run - tag = get_tag(reference_date=parameter_tasks["reference_date"]) - - # Execute notebooks - for key, notebook in notebooks.items(): - parameter_tasks[key] = papermill_execute_notebook( - input_filename=notebook["filename"], - output_tag=tag, - parameters={ - k: parameter_tasks[v] - for k, v in notebook.get("parameters", {}).items() - }, - ) - if "output" in notebook: - # Create PDF report from notebook - convert_notebook_to_pdf( - notebook_path=parameter_tasks[key], - asciidoc_template=notebook["output"]["template"], - ) - - return workflow diff --git a/autoflow/config/asciidoc_extended.tpl b/autoflow/config/asciidoc_extended.tpl deleted file mode 100644 index 7c537e0b75..0000000000 --- a/autoflow/config/asciidoc_extended.tpl +++ /dev/null @@ -1,42 +0,0 @@ -{% extends 'asciidoc/index.asciidoc.j2' %} - -{% block header %} -:imagesdir: {{ resources['output_files_dir'] }} -{% endblock header %} - -{% block input %} -{% if resources.global_content_filter.include_input_prompt %} -{%- endif -%} -{%- if cell['cell_type'] == 'markdown' -%} -{{ cell.source}} -{%- endif -%} -{% endblock input %} - -{% block output_group %} -{% if resources.global_content_filter.include_output_prompt %} -{%- endif %} -{% block outputs %} -{{- super() -}} -{% endblock outputs %} -{% endblock output_group %} - -{% block data_svg %} -image::{{output.metadata.filenames['image/svg+xml'].split('/')[-1]}}[pdfwidth=90%] -{% endblock data_svg %} - -{% block data_png %} -image::{{output.metadata.filenames['image/png'].split('/')[-1]}}[pdfwidth=90%] -{% endblock data_png %} - -{% block data_jpg %} -image::{{output.metadata.filenames['image/jpeg'].split('/')[-1]}}[pdfwidth=90%] -{% endblock data_jpg %} - -{% block stream %} -{% endblock stream %} - -{% block data_text scoped %} ----- -{{ super() }} ----- -{% endblock data_text %} diff --git a/autoflow/config/config.toml b/autoflow/config/config.toml deleted file mode 100644 index 53232c35e0..0000000000 --- a/autoflow/config/config.toml +++ /dev/null @@ -1,15 +0,0 @@ -flowapi_url = "$FLOWAPI_URL" -db_uri = "$AUTOFLOW_DB_URI" - -[inputs] - -inputs_dir = "$AUTOFLOW_INPUTS_DIR" - -[outputs] - -notebooks_dir = "$AUTOFLOW_OUTPUTS_DIR/notebooks" -reports_dir = "$AUTOFLOW_OUTPUTS_DIR/reports" - -[logging] - -level = "$AUTOFLOW_LOG_LEVEL" diff --git a/autoflow/docker-stack.yml b/autoflow/docker-stack.yml deleted file mode 100644 index 13e48db739..0000000000 --- a/autoflow/docker-stack.yml +++ /dev/null @@ -1,51 +0,0 @@ -# -# Example docker stack file for AutoFlow -# - -version: '3.7' - -secrets: - AUTOFLOW_DB_PASSWORD: # Password for AutoFlow's database - external: true - FLOWAPI_TOKEN: # Token for AutoFlow to connect to FlowAPI - external: true - -networks: - autoflow_db: - api: - -services: - flowapi: - networks: - - api - - autoflow: - image: flowminder/autoflow:${CONTAINER_TAG:-latest} - environment: - AUTOFLOW_LOG_LEVEL: ${AUTOFLOW_LOG_LEVEL:-ERROR} - AUTOFLOW_DB_URI: postgresql://autoflow:{}@autoflow_postgres:5432/autoflow - FLOWAPI_URL: https://flowapi:9090 - SSL_CERTIFICATE_FILE: /run/secrets/cert-flowkit.pem - secrets: - - AUTOFLOW_DB_PASSWORD - - FLOWAPI_TOKEN - - cert-flowkit.pem - volumes: - - ${AUTOFLOW_INPUTS_DIR:?}:/mounts/inputs:ro - - ${AUTOFLOW_OUTPUTS_DIR:?}:/mounts/outputs:rw - networks: - - autoflow_db - - api - - autoflow_postgres: - image: postgres - tty: true - stdin_open: true - environment: - POSTGRES_USER: autoflow - POSTGRES_PASSWORD_FILE: /run/secrets/AUTOFLOW_DB_PASSWORD - POSTGRES_DB: autoflow - secrets: - - AUTOFLOW_DB_PASSWORD - networks: - - autoflow_db diff --git a/autoflow/examples/inputs/flows_report.ipynb b/autoflow/examples/inputs/flows_report.ipynb deleted file mode 100644 index 101b751bd2..0000000000 --- a/autoflow/examples/inputs/flows_report.ipynb +++ /dev/null @@ -1,251 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import geopandas as gpd\n", - "import pandas as pd\n", - "import scrapbook as sb\n", - "import matplotlib.pyplot as plt\n", - "from IPython.display import display, Markdown\n", - "from get_secret_or_env_var import environ, getenv\n", - "\n", - "from flowclient import connect, get_result_by_query_id, get_geography\n", - "\n", - "%matplotlib inline" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "parameters" - ] - }, - "outputs": [], - "source": [ - "# Default parameter values\n", - "flowapi_url = \"http://localhost:9090\"\n", - "aggregation_unit = \"admin2\"\n", - "reference_date = \"2016-01-04\"\n", - "previous_notebook = \"run_flows.ipynb\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Connect to FlowAPI\n", - "conn = connect(\n", - " url=flowapi_url,\n", - " token=environ[\"FLOWAPI_TOKEN\"],\n", - " ssl_certificate=getenv(\"SSL_CERTIFICATE_FILE\"),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Read glued data from run_flows notebook\n", - "nb = sb.read_notebook(previous_notebook)\n", - "query_ids = nb.scraps[\"query_ids\"].data\n", - "date_periods = nb.scraps[\"date_periods\"].data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Get cached results\n", - "cache_results = {\n", - " key: get_result_by_query_id(connection=conn, query_id=val)\n", - " for key, val in query_ids.items()\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Get geography data\n", - "agg_unit_geodata = gpd.GeoDataFrame().from_features(\n", - " get_geography(connection=conn, aggregation_unit=aggregation_unit)\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Display title and date periods\n", - "date_periods_string = \"\\n\".join(\n", - " [f\"- **{key}**: [{dates[0]}, {dates[1]}) \" for key, dates in date_periods.items()]\n", - ")\n", - "display(\n", - " Markdown(\n", - " f\"\"\"\n", - "# Above-normal {aggregation_unit} flows: {reference_date}\n", - "## Date periods\n", - "{date_periods_string} \n", - "\"\"\"\n", - " )\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Calculate in/outflows above normal\n", - "flowabnormal = cache_results[\"flowabnormal\"].set_index([\"pcod_from\", \"pcod_to\"])\n", - "flownormal = cache_results[\"flownormal\"].set_index([\"pcod_from\", \"pcod_to\"])\n", - "flows_above_normal = (flowabnormal - flownormal).dropna().reset_index()\n", - "\n", - "inflow_above_normal = (\n", - " flows_above_normal[flows_above_normal.pcod_from != flows_above_normal.pcod_to]\n", - " .groupby(\"pcod_to\")\n", - " .sum()\n", - " .reset_index()\n", - ")\n", - "outflow_above_normal = (\n", - " flows_above_normal[flows_above_normal.pcod_from != flows_above_normal.pcod_to]\n", - " .groupby(\"pcod_from\")\n", - " .sum()\n", - " .reset_index()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Join with geo data\n", - "\n", - "inflow_above_normal = gpd.GeoDataFrame(\n", - " pd.merge(inflow_above_normal, agg_unit_geodata, left_on=\"pcod_to\", right_on=\"pcod\")\n", - ")\n", - "outflow_above_normal = gpd.GeoDataFrame(\n", - " pd.merge(\n", - " outflow_above_normal, agg_unit_geodata, left_on=\"pcod_from\", right_on=\"pcod\"\n", - " )\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Inflows above normal" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Plot inflows above normal\n", - "fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(35, 7))\n", - "agg_unit_geodata.plot(ax=ax, color=\"grey\")\n", - "inflows_plot = inflow_above_normal.plot(ax=ax, column=\"value\", legend=True)\n", - "plt.axis(\"off\")\n", - "fig.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Display table\n", - "inflow_above_normal[[\"value\", \"pcod\"]].style.hide()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Outflows above normal" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Plot outflows above normal\n", - "fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(35, 7))\n", - "agg_unit_geodata.plot(ax=ax, color=\"grey\")\n", - "outflow_above_normal.plot(ax=ax, column=\"value\", legend=True)\n", - "plt.axis(\"off\")\n", - "fig.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Display table\n", - "outflow_above_normal[[\"value\", \"pcod\"]].style.hide()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "flowmachine-KDz5Pcfk", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.9 (default, Oct 26 2021, 07:25:54) \n[Clang 13.0.0 (clang-1300.0.29.30)]" - }, - "pycharm": { - "stem_cell": { - "cell_type": "raw", - "metadata": { - "collapsed": false - }, - "source": [] - } - }, - "vscode": { - "interpreter": { - "hash": "109f0852a007594fdeaccffd602db0694e6441914c3f559989e01dcf6f2ca3cf" - } - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/autoflow/examples/inputs/run_flows.ipynb b/autoflow/examples/inputs/run_flows.ipynb deleted file mode 100644 index bf24b3a9f8..0000000000 --- a/autoflow/examples/inputs/run_flows.ipynb +++ /dev/null @@ -1,177 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from datetime import datetime, timedelta\n", - "import scrapbook as sb\n", - "from get_secret_or_env_var import environ, getenv\n", - "from flowclient import (\n", - " daily_location_spec,\n", - " modal_location_from_dates_spec,\n", - " connect,\n", - " run_query,\n", - " get_result_by_query_id,\n", - ")\n", - "from flowclient.aggregates import flows_spec" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "parameters" - ] - }, - "outputs": [], - "source": [ - "# Default parameter values\n", - "flowapi_url = \"http://localhost:9090\"\n", - "aggregation_unit = \"admin2\"\n", - "date_ranges = [\n", - " [\"2016-01-01\", \"2016-01-03\"],\n", - " [\"2016-01-03\", \"2016-01-04\"],\n", - " [\"2016-01-04\", \"2016-01-05\"],\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Connect to FlowAPI\n", - "conn = connect(\n", - " url=flowapi_url,\n", - " token=environ[\"FLOWAPI_TOKEN\"],\n", - " ssl_certificate=getenv(\"SSL_CERTIFICATE_FILE\"),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define date periods of interest\n", - "periods_of_interest = dict(\n", - " benchmark=date_ranges[0], comparison=date_ranges[1], focal=date_ranges[2]\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Modal location query specifications\n", - "locations = {}\n", - "for key, period in periods_of_interest.items():\n", - " query_spec = modal_location_from_dates_spec(\n", - " start_date=period[0],\n", - " end_date=period[1],\n", - " method=\"last\",\n", - " aggregation_unit=aggregation_unit,\n", - " )\n", - " locations[key] = query_spec" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Flow specifications\n", - "flownormal = flows_spec(\n", - " from_location=locations[\"benchmark\"],\n", - " to_location=locations[\"comparison\"],\n", - ")\n", - "flowabnormal = flows_spec(\n", - " from_location=locations[\"benchmark\"],\n", - " to_location=locations[\"focal\"],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run flows\n", - "res_id_flownormal = run_query(connection=conn, query_spec=flownormal)\n", - "res_id_flowabnormal = run_query(connection=conn, query_spec=flowabnormal)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Get results\n", - "get_result_by_query_id(connection=conn, query_id=res_id_flownormal)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "get_result_by_query_id(connection=conn, query_id=res_id_flowabnormal)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Glue query IDs and date periods so that they can be accessed in other notebooks\n", - "sb.glue(\n", - " \"query_ids\", {\"flownormal\": res_id_flownormal, \"flowabnormal\": res_id_flowabnormal}\n", - ")\n", - "sb.glue(\"date_periods\", periods_of_interest)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.0" - }, - "pycharm": { - "stem_cell": { - "cell_type": "raw", - "metadata": { - "collapsed": false - }, - "source": [] - } - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/autoflow/examples/inputs/workflows.yml b/autoflow/examples/inputs/workflows.yml deleted file mode 100644 index 6821e39f58..0000000000 --- a/autoflow/examples/inputs/workflows.yml +++ /dev/null @@ -1,34 +0,0 @@ -workflows: - - name: flows_above_normal - notebooks: - run_flows: - filename: run_flows.ipynb - parameters: - flowapi_url: flowapi_url - aggregation_unit: aggregation_unit - date_ranges: date_ranges - flows_report: - filename: flows_report.ipynb - parameters: - flowapi_url: flowapi_url - aggregation_unit: aggregation_unit - reference_date: reference_date - previous_notebook: run_flows - output: - format: pdf - -available_dates_sensor: - schedule: "0 0 * * *" - cdr_types: - - calls - - sms - workflows: - - workflow_name: flows_above_normal - parameters: - aggregation_unit: admin2 - earliest_date: 2016-01-04 - date_stencil: [[2016-01-01, 2016-01-03], -1, 0] - - workflow_name: flows_above_normal - parameters: - aggregation_unit: admin1 - date_stencil: [-2, -1, 0] diff --git a/autoflow/examples/outputs/.gitignore b/autoflow/examples/outputs/.gitignore deleted file mode 100644 index 4b92e4fee4..0000000000 --- a/autoflow/examples/outputs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -notebooks/ -reports/ \ No newline at end of file diff --git a/autoflow/pyproject.toml b/autoflow/pyproject.toml deleted file mode 100644 index b0471b7f6a..0000000000 --- a/autoflow/pyproject.toml +++ /dev/null @@ -1,3 +0,0 @@ -[build-system] -requires = ["setuptools", "wheel"] -build-backend = "setuptools.build_meta:__legacy__" \ No newline at end of file diff --git a/autoflow/setup.cfg b/autoflow/setup.cfg deleted file mode 100644 index 26a952ba03..0000000000 --- a/autoflow/setup.cfg +++ /dev/null @@ -1,17 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -[aliases] -test=pytest - -[versioneer] -VCS = git -style = pep440-pre -versionfile_source = autoflow/_version.py -versionfile_build = autoflow/_version.py -tag_prefix = -parentdir_prefix = FlowKit- - -[tool:pytest] -python_files = tests/*/test_*.py tests/test_*.py \ No newline at end of file diff --git a/autoflow/setup.py b/autoflow/setup.py deleted file mode 100644 index ff498d8130..0000000000 --- a/autoflow/setup.py +++ /dev/null @@ -1,50 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import io - -from setuptools import find_packages, setup - -import versioneer - - -with io.open("README.md", "rt", encoding="utf8") as f: - readme = f.read() - -setup( - name="autoflow", - version=versioneer.get_version(), - cmdclass=versioneer.get_cmdclass(), - url="http://github.com/Flowminder/FlowKit", - license="MPLv2", - maintainer="Flowminder", - maintainer_email="flowkit@flowminder.org", - description="AutoFlow automates event-driven Jupyter-notebook-based workflows that interact with FlowAPI.", - long_description=readme, - packages=find_packages(), - include_package_data=True, - zip_safe=False, - install_requires=[ - "flowclient", - "get-secret-or-env-var", - "ipykernel", - "marshmallow >= 3.0.0", - "networkx", - "nteract-scrapbook", - "nbconvert>6.0.0", - "nbformat", - "papermill >= 1.2.1", - "pendulum", - "prefect < 2.0", - "distributed", - "pyyaml", - "sh", - "sqlalchemy<2.0.0", - ], - extras_require={ - "test": ["pytest", "pytest-cov", "sqlalchemy-utils", "testing-postgresql"], - "postgres": ["psycopg2-binary"], - "examples": ["descartes", "geopandas", "matplotlib", "pandas"], - }, -) diff --git a/autoflow/tests/conftest.py b/autoflow/tests/conftest.py deleted file mode 100644 index ac369ccd08..0000000000 --- a/autoflow/tests/conftest.py +++ /dev/null @@ -1,81 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -import logging -import time - -import testing.postgresql -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker - -from autoflow.model import Base, WorkflowRuns - - -@pytest.fixture(scope="session") -def postgres_test_db(): - """ - Fixture to yield a PostgreSQL database to be used in the unit tests. - The database will only be created once per test session, but the - workflow_runs table will be cleared for each test. - """ - postgres_test_db = testing.postgresql.Postgresql() - engine = create_engine(postgres_test_db.url()) - - from sqlalchemy_utils import database_exists, create_database - - if not database_exists(engine.url): - create_database(engine.url) - Base.metadata.create_all(bind=engine, tables=[WorkflowRuns.__table__]) - - yield postgres_test_db - postgres_test_db.stop() - - -@pytest.fixture(scope="function") -def session(postgres_test_db): - """ - Fixture to yield a session to the test PostgreSQL database. This clears - the workflow_runs table for each test. - """ - engine = create_engine(postgres_test_db.url()) - engine.execute(f"TRUNCATE TABLE {WorkflowRuns.__table__};") - Session = sessionmaker(bind=engine) - returned_session = Session() - yield returned_session - returned_session.close() - - -@pytest.fixture(scope="function") -def sqlite_session(): - """ - Fixture to yield a session to a temporary sqlite database. - This creates the workflow_runs table before yielding a session. - """ - engine = create_engine("sqlite:///") - Base.metadata.drop_all(bind=engine, tables=[WorkflowRuns.__table__]) - Base.metadata.create_all(bind=engine, tables=[WorkflowRuns.__table__]) - Session = sessionmaker(bind=engine) - returned_session = Session() - yield returned_session - returned_session.close() - Base.metadata.drop_all(bind=engine, tables=[WorkflowRuns.__table__]) - - -@pytest.fixture(scope="session") -def test_logger(): - """ - Logger to use when testing prefect tasks. - """ - logger = logging.getLogger("autoflow_tests") - handler = logging.StreamHandler() - formatter = logging.Formatter( - "[%(asctime)s] %(levelname)s - %(name)s | %(message)s" - ) - formatter.converter = time.gmtime - handler.setFormatter(formatter) - logger.addHandler(handler) - logger.setLevel(logging.DEBUG) - return logger diff --git a/autoflow/tests/schema_tests/test_available_dates_sensor_schema.py b/autoflow/tests/schema_tests/test_available_dates_sensor_schema.py deleted file mode 100644 index 47cf531bb9..0000000000 --- a/autoflow/tests/schema_tests/test_available_dates_sensor_schema.py +++ /dev/null @@ -1,102 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -from marshmallow import ValidationError -from prefect import Flow, Parameter -from prefect import storage -from prefect.schedules import Schedule - -from autoflow.parser.available_dates_sensor_schema import AvailableDatesSensorSchema -from autoflow.sensor import WorkflowConfig - - -def test_available_dates_sensor_schema(tmpdir): - """ - Test that AvailableDatesSensorSchema can load a valid set of sensor config parameters. - """ - # Create storage object to pass in context - dummy_workflow = Flow(name="DUMMY_WORKFLOW") - for parameter in ["reference_date", "date_ranges", "DUMMY_PARAM"]: - dummy_workflow.add_task(Parameter(parameter)) - workflow_storage = storage.Local(tmpdir) - workflow_storage.add_flow(dummy_workflow) - - input_dict = dict( - schedule="0 0 * * *", - cdr_types=["calls", "sms", "mds", "topups"], - workflows=[ - {"workflow_name": "DUMMY_WORKFLOW", "parameters": {"DUMMY_PARAM": 1}}, - {"workflow_name": "DUMMY_WORKFLOW", "parameters": {"DUMMY_PARAM": 2}}, - ], - ) - sensor_config = AvailableDatesSensorSchema( - context={"workflow_storage": workflow_storage} - ).load(input_dict) - assert isinstance(sensor_config["schedule"], Schedule) - assert sensor_config["schedule"].clocks[0].cron == "0 0 * * *" - assert sensor_config["cdr_types"] == ["calls", "sms", "mds", "topups"] - assert sensor_config["workflows"] == [ - WorkflowConfig(**workflow) for workflow in input_dict["workflows"] - ] - - -def test_available_dates_sensor_schema_null_schedule(): - """ - Test that AvailableDatesSensorSchema can load schedule=None. - """ - input_dict = dict(schedule=None, cdr_types=["calls"], workflows=[]) - sensor_config = AvailableDatesSensorSchema().load(input_dict) - assert sensor_config["schedule"] is None - assert sensor_config["cdr_types"] == ["calls"] - assert sensor_config["workflows"] == [] - - -def test_available_dates_sensor_schema_missing_schedule(): - """ - Test that AvailableDatesSensorSchema raises a ValidationError if the - 'schedule' field is missing. - """ - input_dict = dict(cdr_types=["calls"], workflows=[]) - with pytest.raises(ValidationError) as exc_info: - sensor_config = AvailableDatesSensorSchema().load(input_dict) - assert "Missing data for required field." in exc_info.value.messages["schedule"] - - -def test_available_dates_sensor_schema_missing_cdr_types(): - """ - Test that AvailableDatesSensorSchema 'cdr_types' field loads None if the - input 'cdr_types' field is missing. - """ - input_dict = dict(schedule="0 0 * * *", workflows=[]) - sensor_config = AvailableDatesSensorSchema().load(input_dict) - assert sensor_config["cdr_types"] is None - - -def test_available_dates_sensor_schema_invalid_cdr_types(): - """ - Test that AvailableDatesSensorSchema raises a ValidationError if the - 'cdr_types' field contains invalid values. - """ - input_dict = dict( - schedule="0 0 * * *", cdr_types=["calls", "NOT_A_CDR_TYPE"], workflows=[] - ) - with pytest.raises(ValidationError) as exc_info: - sensor_config = AvailableDatesSensorSchema().load(input_dict) - assert ( - "Must be one of: calls, sms, mds, topups." - in exc_info.value.messages["cdr_types"][1] - ) - - -def test_available_dates_sensor_schema_missing_workflows(): - """ - Test that AvailableDatesSensorSchema raises a ValidationError if the - 'workflows' field is missing. - """ - input_dict = dict(schedule="0 0 * * *", cdr_types=["calls"]) - with pytest.raises(ValidationError) as exc_info: - sensor_config = AvailableDatesSensorSchema().load(input_dict) - assert "Missing data for required field." in exc_info.value.messages["workflows"] diff --git a/autoflow/tests/schema_tests/test_custom_fields.py b/autoflow/tests/schema_tests/test_custom_fields.py deleted file mode 100644 index 223024b919..0000000000 --- a/autoflow/tests/schema_tests/test_custom_fields.py +++ /dev/null @@ -1,96 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -import datetime - -from marshmallow import ValidationError -from prefect.schedules import Schedule - -from autoflow.date_stencil import DateStencil -from autoflow.parser.custom_fields import ( - DateField, - DateStencilField, - ScheduleField, -) - - -def test_schedule_field(): - """ - Test that ScheduleField deserialises a cron string to a Schedule object. - """ - schedule = ScheduleField().deserialize("0 0 * * *") - assert isinstance(schedule, Schedule) - assert schedule.clocks[0].cron == "0 0 * * *" - - -def test_schedule_field_none(): - """ - Test that ScheduleField deserialises None to None. - """ - schedule = ScheduleField(allow_none=True).deserialize(None) - assert schedule is None - - -def test_schedule_field_validation_error(): - """ - Test that ScheduleField raises a ValidationError for an invalid cron string. - """ - with pytest.raises(ValidationError) as exc_info: - schedule = ScheduleField().deserialize("NOT A CRON STRING") - assert "Invalid cron string: 'NOT A CRON STRING'." in exc_info.value.messages - - -def test_date_field_deserialises_date(): - """ - Test that DateField can deserialise a date object. - """ - input_date = datetime.date(2016, 1, 1) - deserialised_date = DateField().deserialize(input_date) - assert deserialised_date == input_date - - -def test_date_field_deserialises_string(): - """ - Test that DateField can deserialise an iso date string to a date object. - """ - deserialised_date = DateField().deserialize("2016-01-01") - assert deserialised_date == datetime.date(2016, 1, 1) - - -def test_date_stencil_field(): - """ - Test that DateStencilField deserialises a raw stencil to a DateStencil object. - """ - raw_stencil = [[datetime.date(2016, 1, 1), datetime.date(2016, 1, 3)], [-2, -1], 0] - date_stencil = DateStencilField().deserialize(raw_stencil) - assert isinstance(date_stencil, DateStencil) - assert date_stencil._intervals == ( - (datetime.date(2016, 1, 1), datetime.date(2016, 1, 3)), - (-2, -1), - (0, 1), - ) - - -@pytest.mark.parametrize( - "raw_stencil,message", - [ - ("NOT_A_LIST", "N is not an integer or date."), - (["BAD_ELEMENT"], "BAD_ELEMENT is not an integer or date."), - ([[-1, "BAD_ELEMENT"]], "BAD_ELEMENT is not an integer or date."), - ( - [[-3, -2, -1]], - "Expected date interval to have length 2 (in format [start, end]), but got sequence of length 3.", - ), - ([[-1, -2]], "Date stencil contains invalid interval (-1, -2)."), - ], -) -def test_date_stencil_field_validation_error(raw_stencil, message): - """ - Test that DateStencilField raises a ValidationError if the raw stencil is not valid. - """ - with pytest.raises(ValidationError) as exc_info: - date_stencil = DateStencilField().deserialize(raw_stencil) - assert message in exc_info.value.messages diff --git a/autoflow/tests/schema_tests/test_notebook_output_schema.py b/autoflow/tests/schema_tests/test_notebook_output_schema.py deleted file mode 100644 index 50b188745b..0000000000 --- a/autoflow/tests/schema_tests/test_notebook_output_schema.py +++ /dev/null @@ -1,78 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -from marshmallow import ValidationError - -from autoflow.parser.notebook_output_schema import NotebookOutputSchema - - -def test_notebook_output_schema(monkeypatch): - """ - Test that NotebookOutputSchema can load a dict of notebook output options. - """ - monkeypatch.setattr("pathlib.Path.exists", lambda self: True) - input_value = {"format": "pdf", "template": "DUMMY_TEMPLATE"} - loaded_value = NotebookOutputSchema( - context={"inputs_dir": "DUMMY_INPUTS_DIR"} - ).load(input_value) - assert loaded_value == input_value - - -def test_notebook_output_schema_no_template(): - """ - Test that NotebookOutputSchema can load notebook output options if - 'template' is not specified. - """ - loaded_value = NotebookOutputSchema().load({"format": "pdf"}) - assert loaded_value == {"format": "pdf", "template": None} - - -def test_notebook_output_schema_no_format(): - """ - Test that NotebookOutputSchema raises a ValidationError if 'format' is not specified. - """ - with pytest.raises(ValidationError) as exc_info: - loaded_value = NotebookOutputSchema().load({}) - assert "Missing data for required field." in exc_info.value.messages["format"] - - -def test_notebook_output_schema_invalid_format(): - """ - Test that NotebookOutputSchema raises a ValidationError if 'format' is not 'pdf'. - """ - with pytest.raises(ValidationError) as exc_info: - loaded_value = NotebookOutputSchema().load({"format": "not_pdf"}) - assert "Must be one of: pdf." in exc_info.value.messages["format"] - - -def test_notebook_output_schema_file_not_found(): - """ - Test that NotebookOutputSchema raises a ValidationError if the template - file doesn't exist. - """ - with pytest.raises(ValidationError) as exc_info: - loaded_value = NotebookOutputSchema( - context={"inputs_dir": "DUMMY_INPUTS_DIR"} - ).load({"format": "pdf", "template": "DUMMY_TEMPLATE"}) - assert ( - "Asciidoc template file 'DUMMY_TEMPLATE' not found." - in exc_info.value.messages["template"] - ) - - -def test_notebook_output_schema_no_context(): - """ - Test that NotebookOutputSchema raises a ValidationError if 'inputs_dir' - isn't provided in the context. - """ - with pytest.raises(ValidationError) as exc_info: - loaded_value = NotebookOutputSchema().load( - {"format": "pdf", "template": "DUMMY_TEMPLATE"} - ) - assert ( - "'inputs_dir' was not provided in the context. Cannot check for file existence." - in exc_info.value.messages["template"] - ) diff --git a/autoflow/tests/schema_tests/test_notebook_schema.py b/autoflow/tests/schema_tests/test_notebook_schema.py deleted file mode 100644 index dc36779c3b..0000000000 --- a/autoflow/tests/schema_tests/test_notebook_schema.py +++ /dev/null @@ -1,116 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -from marshmallow import ValidationError - -from autoflow.parser.notebook_schema import NotebookSchema - - -def test_notebook_schema(monkeypatch): - """ - Test that NotebookSchema can load a notebook task specification. - """ - monkeypatch.setattr("pathlib.Path.exists", lambda self: True) - input_value = { - "filename": "DUMMY_NOTEBOOK.ipynb", - "parameters": {"DUMMY_PARAM": "DUMMY_VALUE"}, - "output": {"format": "pdf", "template": "DUMMY_TEMPLATE"}, - } - loaded_value = NotebookSchema(context={"inputs_dir": "DUMMY_INPUTS_DIR"}).load( - input_value - ) - assert loaded_value == input_value - - -def test_notebook_schema_optional_fields(monkeypatch): - """ - Test that NotebookSchema can load a notebook task specification without - 'parameters' or 'output' specified. - """ - monkeypatch.setattr("pathlib.Path.exists", lambda self: True) - input_value = {"filename": "DUMMY_NOTEBOOK.ipynb"} - loaded_value = NotebookSchema(context={"inputs_dir": "DUMMY_INPUTS_DIR"}).load( - input_value - ) - assert loaded_value == input_value - - -def test_notebook_schema_no_filename(monkeypatch): - """ - Test that NotebookSchema raises a ValidationError if filename is not provided. - """ - input_value = {"parameters": {"DUMMY_PARAM": "DUMMY_VALUE"}} - with pytest.raises(ValidationError) as exc_info: - loaded_value = NotebookSchema().load(input_value) - assert "Missing data for required field." in exc_info.value.messages["filename"] - - -def test_notebook_schema_invalid_filename(monkeypatch): - """ - Test that NotebookSchema raises a ValidationError if the file is not a Jupyter notebook. - """ - monkeypatch.setattr("pathlib.Path.exists", lambda self: True) - input_value = {"filename": "NOT_A_NOTEBOOK.html"} - with pytest.raises(ValidationError) as exc_info: - loaded_value = NotebookSchema(context={"inputs_dir": "DUMMY_INPUTS_DIR"}).load( - input_value - ) - assert ( - "File 'NOT_A_NOTEBOOK.html' is not a Jupyter notebook." - in exc_info.value.messages["filename"] - ) - - -def test_notebook_schema_file_not_found(): - """ - Test that NotebookSchema raises a ValidationError if the file does not exist. - """ - input_value = {"filename": "DUMMY_NOTEBOOK.ipynb"} - with pytest.raises(ValidationError) as exc_info: - loaded_value = NotebookSchema(context={"inputs_dir": "DUMMY_INPUTS_DIR"}).load( - input_value - ) - assert ( - "Notebook 'DUMMY_NOTEBOOK.ipynb' not found." - in exc_info.value.messages["filename"] - ) - - -def test_notebook_schema_no_context(): - """ - Test that NotebookSchema raises a ValidationError if 'inputs_dir' - isn't provided in the context. - """ - input_value = {"filename": "DUMMY_NOTEBOOK.ipynb"} - with pytest.raises(ValidationError) as exc_info: - loaded_value = NotebookSchema().load(input_value) - assert ( - "'inputs_dir' was not provided in the context. Cannot check for file existence." - in exc_info.value.messages["filename"] - ) - - -def test_notebook_schema_parameters_keys(): - """ - Test that NotebookSchema raises a ValidationError if parameter keys are not strings. - """ - input_value = {"filename": "DUMMY_NOTEBOOK.ipynb", "parameters": {1: "DUMMY_VALUE"}} - with pytest.raises(ValidationError) as exc_info: - loaded_value = NotebookSchema().load(input_value) - assert "Not a valid string." in exc_info.value.messages["parameters"][1]["key"] - - -def test_notebook_schema_parameters_values(): - """ - Test that NotebookSchema raises a ValidationError if parameter values are not strings. - """ - input_value = {"filename": "DUMMY_NOTEBOOK.ipynb", "parameters": {"DUMMY_PARAM": 1}} - with pytest.raises(ValidationError) as exc_info: - loaded_value = NotebookSchema().load(input_value) - assert ( - "Not a valid string." - in exc_info.value.messages["parameters"]["DUMMY_PARAM"]["value"] - ) diff --git a/autoflow/tests/schema_tests/test_notebooks_field.py b/autoflow/tests/schema_tests/test_notebooks_field.py deleted file mode 100644 index 67de268da5..0000000000 --- a/autoflow/tests/schema_tests/test_notebooks_field.py +++ /dev/null @@ -1,109 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -from collections import OrderedDict - -from marshmallow import Schema, ValidationError - -from autoflow.parser.notebooks_field import NotebooksField - - -def test_notebooks_field_init_does_not_accept_keys(): - """ - Test that NotebooksField.__init__ does not accept a 'keys' argument. - """ - with pytest.raises( - TypeError, match="The Notebooks field does not accept a 'keys' argument." - ): - field = NotebooksField(keys=str) - - -def test_notebooks_field_init_does_not_accept_values(): - """ - Test that NotebooksField.__init__ does not accept a 'values' argument. - """ - with pytest.raises( - TypeError, match="The Notebooks field does not accept a 'values' argument." - ): - field = NotebooksField(values=str) - - -def test_notebooks_field_deserialise(monkeypatch): - """ - Test that NotebooksField deserialises a dict of notebook specifications as - an OrderedDict. - """ - monkeypatch.setattr("pathlib.Path.exists", lambda self: True) - notebooks = { - "notebook1": {"filename": "NOTEBOOK1.ipynb"}, - "notebook2": { - "filename": "NOTEBOOK2.ipynb", - "parameters": {"other_notebook": "notebook1"}, - }, - } - notebooks_field = NotebooksField() - # Can't set context directly on a Field - must be set on the parent Schema - notebooks_field._bind_to_schema( - "notebooks", Schema(context={"inputs_dir": "DUMMY_INPUTS_DIR"}) - ) - deserialised_notebooks = notebooks_field.deserialize(notebooks) - assert isinstance(deserialised_notebooks, OrderedDict) - assert deserialised_notebooks == notebooks - - -@pytest.mark.parametrize( - "key,message", - [ - (123, "Not a valid string."), - ("reference_date", "Invalid input."), - ("date_ranges", "Invalid input."), - ("flowapi_url", "Invalid input."), - ], -) -def test_notebooks_field_invalid_keys(monkeypatch, key, message): - """ - Test that NotebooksField raises a ValidationError if a notebook key is not - a string, or has a disallowed value. - """ - monkeypatch.setattr("pathlib.Path.exists", lambda self: True) - notebooks = {key: {"filename": "NOTEBOOK1.ipynb"}} - notebooks_field = NotebooksField() - # Can't set context directly on a Field - must be set on the parent Schema - notebooks_field._bind_to_schema( - "notebooks", Schema(context={"inputs_dir": "DUMMY_INPUTS_DIR"}) - ) - with pytest.raises(ValidationError) as exc_info: - deserialised_notebooks = notebooks_field.deserialize(notebooks) - assert message in exc_info.value.messages[key]["key"] - - -def test_notebooks_field_circular_dependency(monkeypatch): - """ - Test that NotebooksField raises a ValidationError if notebook - specifications have circular dependencies. - """ - monkeypatch.setattr("pathlib.Path.exists", lambda self: True) - notebooks = { - "notebook1": { - "filename": "NOTEBOOK1.ipynb", - "parameters": {"param": "notebook2"}, - }, - "notebook2": { - "filename": "NOTEBOOK2.ipynb", - "parameters": {"param": "notebook1"}, - }, - } - notebooks_field = NotebooksField() - # Can't set context directly on a Field - must be set on the parent Schema - notebooks_field._bind_to_schema( - "notebooks", Schema(context={"inputs_dir": "DUMMY_INPUTS_DIR"}) - ) - with pytest.raises(ValidationError) as exc_info: - deserialised_notebooks = notebooks_field.deserialize(notebooks) - assert ( - "Notebook specifications contain circular dependencies." - in exc_info.value.messages - ) diff --git a/autoflow/tests/schema_tests/test_workflow_config_schema.py b/autoflow/tests/schema_tests/test_workflow_config_schema.py deleted file mode 100644 index 3a15b80ff7..0000000000 --- a/autoflow/tests/schema_tests/test_workflow_config_schema.py +++ /dev/null @@ -1,222 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -import datetime - -from marshmallow import ValidationError -from prefect import Flow, Parameter -from prefect import storage - -from autoflow.date_stencil import DateStencil -from autoflow.parser.workflow_config_schema import WorkflowConfigSchema -from autoflow.sensor import WorkflowConfig - - -def test_workflow_config_schema(tmpdir): - """ - Test that WorkflowConfigSchema loads input data into a WorkflowConfig namedtuple. - """ - # Create storage object to pass in context - dummy_workflow = Flow(name="DUMMY_WORKFLOW") - for parameter in ["reference_date", "date_ranges", "DUMMY_PARAM"]: - dummy_workflow.add_task(Parameter(parameter)) - workflow_storage = storage.Local(tmpdir) - workflow_storage.add_flow(dummy_workflow) - - input_dict = dict( - workflow_name="DUMMY_WORKFLOW", - parameters={"DUMMY_PARAM": "DUMMY_VALUE"}, - earliest_date=datetime.date(2016, 1, 1), - date_stencil=[-1, 0], - ) - workflow_config = WorkflowConfigSchema( - context={"workflow_storage": workflow_storage} - ).load(input_dict) - assert isinstance(workflow_config, WorkflowConfig) - assert workflow_config.workflow_name == input_dict["workflow_name"] - assert workflow_config.parameters == input_dict["parameters"] - assert workflow_config.earliest_date == input_dict["earliest_date"] - assert workflow_config.date_stencil == DateStencil(input_dict["date_stencil"]) - - -def test_workflow_config_schema_defaults(tmpdir): - """ - Test that WorkflowConfigSchema loads input data if 'parameters', - 'earliest_date' and 'date_stencil' are not specified. - """ - # Create storage object to pass in context - dummy_workflow = Flow(name="DUMMY_WORKFLOW") - for parameter in ["reference_date", "date_ranges"]: - dummy_workflow.add_task(Parameter(parameter)) - workflow_storage = storage.Local(tmpdir) - workflow_storage.add_flow(dummy_workflow) - - input_dict = dict(workflow_name="DUMMY_WORKFLOW") - workflow_config = WorkflowConfigSchema( - context={"workflow_storage": workflow_storage} - ).load(input_dict) - assert isinstance(workflow_config, WorkflowConfig) - assert workflow_config == WorkflowConfig(workflow_name="DUMMY_WORKFLOW") - - -def test_workflow_config_schema_missing_workflow_name(): - """ - Test that WorkflowConfigSchema raises a ValidationError if 'workflow_name' - is not provided. - """ - input_dict = dict( - parameters={"DUMMY_PARAM": "DUMMY_VALUE"}, - earliest_date=datetime.date(2016, 1, 1), - date_stencil=[-1, 0], - ) - with pytest.raises(ValidationError) as exc_info: - workflow_config = WorkflowConfigSchema().load(input_dict) - assert ( - "Missing data for required field." in exc_info.value.messages["workflow_name"] - ) - - -def test_workflow_config_schema_invalid_workflow_name(): - """ - Test that WorkflowConfigSchema raises a ValidationError if 'workflow_name' - is not a string. - """ - input_dict = dict(workflow_name=123) - with pytest.raises(ValidationError) as exc_info: - workflow_config = WorkflowConfigSchema().load({"workflow_name": 123}) - assert "Not a valid string." in exc_info.value.messages["workflow_name"] - - -@pytest.mark.parametrize("key", ["reference_date", "date_ranges"]) -def test_workflow_config_schema_invalid_parameter_names(key, tmpdir): - """ - Test that WorkflowConfigSchema raises a ValidationError if the 'parameters' - dict keys contain 'reference_date' or 'date_ranges'. - """ - # Create storage object to pass in context - dummy_workflow = Flow(name="DUMMY_WORKFLOW") - for parameter in ["reference_date", "date_ranges"]: - dummy_workflow.add_task(Parameter(parameter)) - workflow_storage = storage.Local(tmpdir) - workflow_storage.add_flow(dummy_workflow) - - input_dict = dict(workflow_name="DUMMY_WORKFLOW", parameters={key: "DUMMY_VALUE"}) - with pytest.raises(ValidationError) as exc_info: - workflow_config = WorkflowConfigSchema( - context={"workflow_storage": workflow_storage} - ).load(input_dict) - assert "Invalid input." in exc_info.value.messages["parameters"][key]["key"] - - -def test_workflow_config_schema_invalid_earliest_date(tmpdir): - """ - Test that WorkflowConfigSchema raises a ValidationError if the - 'earliest_date' field is not a date. - """ - # Create storage object to pass in context - dummy_workflow = Flow(name="DUMMY_WORKFLOW") - for parameter in ["reference_date", "date_ranges"]: - dummy_workflow.add_task(Parameter(parameter)) - workflow_storage = storage.Local(tmpdir) - workflow_storage.add_flow(dummy_workflow) - - input_dict = dict(workflow_name="DUMMY_WORKFLOW", earliest_date=datetime.time(11)) - with pytest.raises(ValidationError) as exc_info: - workflow_config = WorkflowConfigSchema( - context={"workflow_storage": workflow_storage} - ).load(input_dict) - assert "Not a valid date." in exc_info.value.messages["earliest_date"] - - -def test_workflow_config_schema_workflow_not_found(tmpdir): - """ - Test that WorkflowConfigSchema raises a ValidationError if the named - workflow does not exist. - """ - # Create storage object to pass in context - dummy_workflow = Flow(name="DUMMY_WORKFLOW") - for parameter in ["reference_date", "date_ranges"]: - dummy_workflow.add_task(Parameter(parameter)) - workflow_storage = storage.Local(tmpdir) - workflow_storage.add_flow(dummy_workflow) - - with pytest.raises(ValidationError) as exc_info: - workflow_config = WorkflowConfigSchema( - context={"workflow_storage": workflow_storage} - ).load({"workflow_name": "NONEXISTENT_WORKFLOW"}) - assert ( - "Workflow does not exist in this storage." - in exc_info.value.messages["workflow_name"] - ) - - -@pytest.mark.parametrize("missing_parameter", ["reference_date", "date_ranges"]) -def test_workflow_config_schema_workflow_does_not_accept_automatic_parameters( - missing_parameter, tmpdir -): - """ - Test that WorkflowConfigSchema raises a ValidationError if the named - workflow does not accept parameters 'reference_date' and 'date_ranges'. - """ - # Create storage object to pass in context - dummy_workflow = Flow(name="DUMMY_WORKFLOW") - for parameter in {"reference_date", "date_ranges"} - {missing_parameter}: - dummy_workflow.add_task(Parameter(parameter)) - workflow_storage = storage.Local(tmpdir) - workflow_storage.add_flow(dummy_workflow) - - with pytest.raises(ValidationError) as exc_info: - workflow_config = WorkflowConfigSchema( - context={"workflow_storage": workflow_storage} - ).load({"workflow_name": "DUMMY_WORKFLOW"}) - assert ( - f"Workflow does not accept parameters {{'{missing_parameter}'}}." - in exc_info.value.messages["workflow_name"] - ) - - -def test_workflow_config_schema_no_context(): - """ - Test that WorkflowConfigSchema raises a ValidationError if - 'workflow_storage' is not provided in the schema. - """ - with pytest.raises(ValidationError) as exc_info: - workflow_config = WorkflowConfigSchema().load( - {"workflow_name": "DUMMY_WORKFLOW"} - ) - assert ( - f"'workflow_storage' was not provided in the context. Cannot check for workflow existence." - in exc_info.value.messages["workflow_name"] - ) - - -def test_workflow_config_schema_missing_and_unexpected_parameter_names(tmpdir): - """ - Test that WorkflowConfigSchema raises a ValidationError if the names of the - provided parameters are not valid for the named workflow. - """ - # Create storage object to pass in context - dummy_workflow = Flow(name="DUMMY_WORKFLOW") - for parameter in ["reference_date", "date_ranges", "DUMMY_PARAM"]: - dummy_workflow.add_task(Parameter(parameter)) - workflow_storage = storage.Local(tmpdir) - workflow_storage.add_flow(dummy_workflow) - - input_dict = dict( - workflow_name="DUMMY_WORKFLOW", parameters={"EXTRA_PARAM": "DUMMY_VALUE"} - ) - with pytest.raises(ValidationError) as exc_info: - workflow_config = WorkflowConfigSchema( - context={"workflow_storage": workflow_storage} - ).load(input_dict) - assert ( - "Missing required parameters {'DUMMY_PARAM'} for workflow 'DUMMY_WORKFLOW'." - in exc_info.value.messages["parameters"] - ) - assert ( - "Unexpected parameters provided for workflow 'DUMMY_WORKFLOW': {'EXTRA_PARAM'}." - in exc_info.value.messages["parameters"] - ) diff --git a/autoflow/tests/schema_tests/test_workflow_schema.py b/autoflow/tests/schema_tests/test_workflow_schema.py deleted file mode 100644 index 882009f29c..0000000000 --- a/autoflow/tests/schema_tests/test_workflow_schema.py +++ /dev/null @@ -1,141 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -from marshmallow import ValidationError -from prefect import storage - -from autoflow.parser.workflow_schema import WorkflowSchema - - -def test_workflow_schema(monkeypatch, tmp_path): - """ - Test that WorkflowSchema loads a workflow specification as a Storage object - containing the defined flow. - """ - monkeypatch.setattr("pathlib.Path.exists", lambda self: True) - workflow = { - "name": "DUMMY_WORKFLOW", - "notebooks": {"notebook1": {"filename": "NOTEBOOK1.ipynb"}}, - } - workflow_storage = WorkflowSchema( - context={ - "inputs_dir": "DUMMY_INPUTS_DIR", - "workflow_storage_dir": str(tmp_path), - } - ).load(workflow) - assert isinstance(workflow_storage, storage.Storage) - assert "DUMMY_WORKFLOW" in workflow_storage - - -def test_workflow_schema_missing_name(monkeypatch, tmp_path): - """ - Test that WorkflowSchema raises a ValidationError if the 'name' field is missing. - """ - monkeypatch.setattr("pathlib.Path.exists", lambda self: True) - workflow = {"notebooks": {"notebook1": {"filename": "NOTEBOOK1.ipynb"}}} - with pytest.raises(ValidationError) as exc_info: - workflow_storage = WorkflowSchema( - context={ - "inputs_dir": "DUMMY_INPUTS_DIR", - "workflow_storage_dir": str(tmp_path), - } - ).load(workflow) - assert "Missing data for required field." in exc_info.value.messages["name"] - - -def test_workflow_schema_invalid_name(monkeypatch, tmp_path): - """ - Test that WorkflowSchema raises a ValidationError if the 'name' field is not a string. - """ - monkeypatch.setattr("pathlib.Path.exists", lambda self: True) - workflow = { - "name": 123, - "notebooks": {"notebook1": {"filename": "NOTEBOOK1.ipynb"}}, - } - with pytest.raises(ValidationError) as exc_info: - workflow_storage = WorkflowSchema( - context={ - "inputs_dir": "DUMMY_INPUTS_DIR", - "workflow_storage_dir": str(tmp_path), - } - ).load(workflow) - assert "Not a valid string." in exc_info.value.messages["name"] - - -def test_workflow_schema_missing_notebooks(monkeypatch, tmp_path): - """ - Test that WorkflowSchema raises a ValidationError if the 'notebooks' field is missing. - """ - monkeypatch.setattr("pathlib.Path.exists", lambda self: True) - workflow = {"name": "DUMMY_WORKFLOW"} - with pytest.raises(ValidationError) as exc_info: - workflow_storage = WorkflowSchema( - context={ - "inputs_dir": "DUMMY_INPUTS_DIR", - "workflow_storage_dir": str(tmp_path), - } - ).load(workflow) - assert "Missing data for required field." in exc_info.value.messages["notebooks"] - - -def test_workflow_schema_many(monkeypatch, tmp_path): - """ - Test that WorkflowSchema can load multiple workflow specifications as a - single Storage object. - """ - monkeypatch.setattr("pathlib.Path.exists", lambda self: True) - workflows = [ - { - "name": "DUMMY_WORKFLOW_1", - "notebooks": {"notebook1": {"filename": "NOTEBOOK1.ipynb"}}, - }, - { - "name": "DUMMY_WORKFLOW_2", - "notebooks": {"notebook2": {"filename": "NOTEBOOK2.ipynb"}}, - }, - ] - workflow_storage = WorkflowSchema( - many=True, - context={ - "inputs_dir": "DUMMY_INPUTS_DIR", - "workflow_storage_dir": str(tmp_path), - }, - ).load(workflows) - assert isinstance(workflow_storage, storage.Storage) - assert "DUMMY_WORKFLOW_1" in workflow_storage - assert "DUMMY_WORKFLOW_2" in workflow_storage - - -def test_workflow_schema_duplicate_name(monkeypatch, tmp_path): - """ - Test that WorkflowSchema raises a ValidationError if multiple workflows - have the same name. - """ - monkeypatch.setattr("pathlib.Path.exists", lambda self: True) - workflows = [ - { - "name": "DUMMY_WORKFLOW", - "notebooks": {"notebook1": {"filename": "NOTEBOOK1.ipynb"}}, - }, - { - "name": "DUMMY_WORKFLOW", - "notebooks": {"notebook2": {"filename": "NOTEBOOK2.ipynb"}}, - }, - { - "name": "DUMMY_WORKFLOW", - "notebooks": {"notebook3": {"filename": "NOTEBOOK3.ipynb"}}, - }, - ] - with pytest.raises(ValidationError) as exc_info: - workflow_storage = WorkflowSchema( - many=True, - context={ - "inputs_dir": "DUMMY_INPUTS_DIR", - "workflow_storage_dir": str(tmp_path), - }, - ).load(workflows) - assert "Duplicate workflow name." in exc_info.value.messages[1]["name"] - assert "Duplicate workflow name." in exc_info.value.messages[2]["name"] diff --git a/autoflow/tests/test_date_stencil.py b/autoflow/tests/test_date_stencil.py deleted file mode 100644 index f1b6883258..0000000000 --- a/autoflow/tests/test_date_stencil.py +++ /dev/null @@ -1,168 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -import datetime - -import pendulum - -from autoflow.date_stencil import DateStencil, InvalidDateIntervalError - - -def test_date_stencil_intervals(): - """ - Test that a DateStencil contains the date intervals defined in the raw stencil passed to __init__. - """ - raw_stencil = [ - pendulum.date(2016, 1, 1), - -4, - [pendulum.date(2016, 1, 2), pendulum.date(2016, 1, 3)], - [-3, -1], - [pendulum.date(2016, 1, 1), -1], - [0, 1], - ] - expected_intervals = ( - (pendulum.date(2016, 1, 1), pendulum.date(2016, 1, 2)), - (-4, -3), - (pendulum.date(2016, 1, 2), pendulum.date(2016, 1, 3)), - (-3, -1), - (pendulum.date(2016, 1, 1), -1), - (0, 1), - ) - date_stencil = DateStencil(raw_stencil) - assert date_stencil._intervals == expected_intervals - - -@pytest.mark.parametrize( - "raw_stencil,error", - [ - (["BAD_ELEMENT"], TypeError), - ([[-1, "BAD_ELEMENT"]], TypeError), - (-1, TypeError), - ([[-3, -2, -1]], ValueError), - ([[-1, -2]], InvalidDateIntervalError), - ([[-1, -1]], InvalidDateIntervalError), - ( - [[pendulum.date(2016, 1, 1), pendulum.date(2016, 1, 1)]], - InvalidDateIntervalError, - ), - ], -) -def test_date_stencil_errors(raw_stencil, error): - """ - Test that DateStencil raises the appropriate error when initialised with an invalid raw stencil. - """ - with pytest.raises(error): - date_stencil = DateStencil(raw_stencil) - - -@pytest.mark.parametrize( - "offset,reference_date,expected", - [ - ( - datetime.date(2016, 1, 2), - pendulum.date(2016, 1, 1), - pendulum.date(2016, 1, 2), - ), - (-2, pendulum.date(2016, 1, 1), pendulum.date(2015, 12, 30)), - (1, datetime.date(2016, 1, 1), pendulum.date(2016, 1, 2)), - ], -) -def test_offset_to_date(offset, reference_date, expected): - """ - Test that DateStencil._offset_to_date returns the expected Date object. - """ - offset_date = DateStencil._offset_to_date(offset, reference_date) - assert isinstance(offset_date, pendulum.Date) - assert offset_date == expected - - -def test_offset_to_date_raises_type_error(): - """ - Test that offset_to_date raises a TypeError if offset has an invalid type. - """ - with pytest.raises( - TypeError, match="Invalid type for offset: expected 'date' or 'int', not 'str'" - ): - offset_date = DateStencil._offset_to_date( - "NOT_AN_OFFSET_OR_DATE", pendulum.date(2016, 1, 1) - ) - - -def test_as_date_pairs(): - """ - Test that DateStencil.as_date_pairs returns expected date pairs. - """ - date_stencil = DateStencil( - [ - -1, - pendulum.date(2016, 1, 1), - [-1, 0], - [pendulum.date(2016, 1, 1), 0], - [-1, pendulum.date(2016, 1, 2)], - [pendulum.date(2016, 1, 1), pendulum.date(2016, 1, 2)], - ] - ) - reference_date = pendulum.date(2016, 1, 2) - date_pairs = date_stencil.as_date_pairs(reference_date) - assert len(date_pairs) == 6 - assert all( - pair == (pendulum.date(2016, 1, 1), pendulum.date(2016, 1, 2)) - for pair in date_pairs - ) - - -def test_as_date_pairs_errors(): - """ - Test that DateStencil.as_date_pairs raises an error if the stencil contains - an invalid interval for the given reference date. - """ - date_stencil = DateStencil([[0, pendulum.date(2016, 1, 1)]]) - with pytest.raises(InvalidDateIntervalError): - date_pairs = date_stencil.as_date_pairs( - reference_date=pendulum.date(2016, 1, 1) - ) - - -def test_as_set_of_dates(): - """ - Test that DateStencil.as_set_of_dates returns expected set of dates. - """ - reference_date = pendulum.date(2016, 1, 7) - date_stencil = DateStencil([[pendulum.date(2016, 1, 1), -3], -1, 0]) - expected_set = set(pendulum.date(2016, 1, d) for d in [1, 2, 3, 6, 7]) - set_of_dates = date_stencil.as_set_of_dates(reference_date) - assert set_of_dates == expected_set - - -@pytest.mark.parametrize( - "reference_date,available", - [ - (pendulum.date(2016, 1, 5), False), - (pendulum.date(2016, 1, 6), True), - (pendulum.date(2016, 1, 7), False), - ], -) -def test_dates_are_available(reference_date, available): - """ - Test that DateStencil.dates_are_available correctly reports whether all dates in the stencil are available. - """ - date_stencil = DateStencil([[pendulum.date(2016, 1, 2), -3], [-3, 0]]) - available_dates = [pendulum.date(2016, 1, d) for d in range(1, 6)] - assert available == date_stencil.dates_are_available( - reference_date, available_dates - ) - - -def test_date_stencil_eq(): - """ - Test that a DateStencil is equal to another DateStencil created using the same raw stencil, - is not equal to a DateStencil created using a different raw stencil, - and is not equal to an object that isn't a DateStencil. - """ - date_stencil = DateStencil([-1, 0]) - assert date_stencil == DateStencil([-1, 0]) - assert date_stencil != DateStencil([-2, 0]) - assert date_stencil != "NOT_A_DATE_STENCIL" diff --git a/autoflow/tests/test_model.py b/autoflow/tests/test_model.py deleted file mode 100644 index 845bf90141..0000000000 --- a/autoflow/tests/test_model.py +++ /dev/null @@ -1,160 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -from unittest.mock import patch - -import pendulum -from sqlalchemy import create_engine -from sqlalchemy.exc import DataError -from sqlalchemy.orm import sessionmaker - -from autoflow.model import Base, WorkflowRuns, RunState, init_db -from autoflow.utils import get_params_hash - - -def test_set_state(session): - """ - Make sure we can add a row to the DB and the content - we expect is in the DB afterwards. - """ - workflow_run_data = dict( - workflow_name="DUMMY_WORKFLOW_NAME", - parameters={"DUMMY_PARAM_NAME": "DUMMY_PARAM_VALUE"}, - state=RunState.running, - ) - - now = pendulum.parse("2016-01-02T13:00:01Z") - with patch("pendulum.now", lambda x: now): - WorkflowRuns.set_state(**workflow_run_data, session=session) - - rows = session.query(WorkflowRuns).all() - assert len(rows) == 1 - - row = rows[0] - assert row.workflow_name == workflow_run_data["workflow_name"] - assert row.parameters_hash == get_params_hash(workflow_run_data["parameters"]) - assert row.state == workflow_run_data["state"] - assert pendulum.instance(row.timestamp) == now - - -def test_set_state_with_sqlite(sqlite_session): - """ - Make sure we can add a row to a sqlite DB. - """ - workflow_run_data = dict( - workflow_name="DUMMY_WORKFLOW_NAME", - parameters={"DUMMY_PARAM_NAME": "DUMMY_PARAM_VALUE"}, - state=RunState.running, - ) - - now = pendulum.parse("2016-01-02T13:00:01Z") - with patch("pendulum.now", lambda x: now): - WorkflowRuns.set_state(**workflow_run_data, session=sqlite_session) - - rows = sqlite_session.query(WorkflowRuns).all() - assert len(rows) == 1 - - row = rows[0] - assert row.workflow_name == workflow_run_data["workflow_name"] - assert row.parameters_hash == get_params_hash(workflow_run_data["parameters"]) - assert row.state == workflow_run_data["state"] - assert pendulum.instance(row.timestamp) == now - - -def test_exception_raised_with_invalid_state(session): - """ - Test that we get an exception raised when we try - to add a new row with an invalid state. - """ - workflow_run_data = dict( - workflow_name="DUMMY_WORKFLOW_NAME", - parameters={"DUMMY_PARAM_NAME": "DUMMY_PARAM_VALUE"}, - state="INVALID_STATE", - ) - with pytest.raises(DataError): - WorkflowRuns.set_state(**workflow_run_data, session=session) - - -def test_get_most_recent_state(session): - """ - Test that get_most_recent_state returns the most recent state. - """ - workflow_run_data = dict( - workflow_name="DUMMY_WORKFLOW_NAME", - parameters={"DUMMY_PARAM_NAME": "DUMMY_PARAM_VALUE"}, - ) - WorkflowRuns.set_state(**workflow_run_data, state=RunState.running, session=session) - WorkflowRuns.set_state(**workflow_run_data, state=RunState.failed, session=session) - - state = WorkflowRuns.get_most_recent_state(**workflow_run_data, session=session) - - assert state == RunState.failed - - -def test_get_most_recent_state_returns_None(session): - """ - Test that get_most_recent_state returns None if a workflow run has no previous state. - """ - workflow_run_data = dict( - workflow_name="DUMMY_WORKFLOW_NAME", - parameters={"DUMMY_PARAM_NAME": "DUMMY_PARAM_VALUE"}, - ) - - state = WorkflowRuns.get_most_recent_state( - workflow_name="DUMMY_WORKFLOW_NAME", - parameters={"DUMMY_PARAM_NAME": "DUMMY_PARAM_VALUE"}, - session=session, - ) - - assert state is None - - -def test_init_db_doesnt_wipe(postgres_test_db): - """ - DB shouldn't get reinitialised if already built. - """ - # Write a row to the workflow_runs table - engine = create_engine(postgres_test_db.url()) - Session = sessionmaker(bind=engine) - session = Session() - WorkflowRuns.set_state( - workflow_name="DUMMY_WORKFLOW_NAME", - parameters={"DUMMY_PARAM_NAME": "DUMMY_PARAM_VALUE"}, - state=RunState.running, - session=session, - ) - session.commit() - session.close() - # Init DB - init_db(postgres_test_db.url()) - # Table should still contain data - session = Session() - assert len(session.query(WorkflowRuns).all()) > 0 - session.close() - - -def test_init_db_force(postgres_test_db): - """ - DB should be wiped clean if force is true. - """ - # Write a row to the workflow_runs table - engine = create_engine(postgres_test_db.url()) - Session = sessionmaker(bind=engine) - session = Session() - WorkflowRuns.set_state( - workflow_name="DUMMY_WORKFLOW_NAME", - parameters={"DUMMY_PARAM_NAME": "DUMMY_PARAM_VALUE"}, - state=RunState.running, - session=session, - ) - session.commit() - session.close() - # Init DB - init_db(postgres_test_db.url(), force=True) - # Table should not contain data - session = Session() - assert len(session.query(WorkflowRuns).all()) == 0 - session.close() diff --git a/autoflow/tests/test_parser.py b/autoflow/tests/test_parser.py deleted file mode 100644 index 1cf0f3a602..0000000000 --- a/autoflow/tests/test_parser.py +++ /dev/null @@ -1,141 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -import datetime -from textwrap import dedent - -from prefect import storage -from prefect.schedules import Schedule - -from autoflow.date_stencil import DateStencil -from autoflow.parser import parse_workflows_yaml -from autoflow.sensor import WorkflowConfig - - -def test_parse_workflows_yaml(tmp_path): - """ - Test that parse_workflows_yaml correctly parses an example input file. - """ - (tmp_path / "notebook1.ipynb").touch() - (tmp_path / "notebook2.ipynb").touch() - (tmp_path / "notebook3.ipynb").touch() - (tmp_path / "custom_template.tpl").touch() - (tmp_path / "dummy_input.yml").write_text( - dedent( - """\ - workflows: - - name: workflow1 - notebooks: - notebook1: - filename: notebook1.ipynb - parameters: - url: flowapi_url - date: reference_date - extra: dummy_param - notebook2: - filename: notebook2.ipynb - parameters: - ranges: date_ranges - other: notebook1 - output: - format: pdf - template: custom_template.tpl - - name: workflow2 - notebooks: - the_notebook: - filename: notebook3.ipynb - output: - format: pdf - - available_dates_sensor: - schedule: "0 0 * * *" - cdr_types: - - calls - - sms - workflows: - - workflow_name: workflow1 - parameters: - dummy_param: 123 - earliest_date: 2016-01-01 - date_stencil: [[2016-01-01, 2016-01-03], -1, 0] - - workflow_name: workflow2 - """ - ) - ) - workflow_storage, sensor_config = parse_workflows_yaml( - filename="dummy_input.yml", - inputs_dir=str(tmp_path), - workflow_storage_dir=str(tmp_path / ".prefect/flows"), - ) - assert isinstance(workflow_storage, storage.Storage) - assert "workflow1" in workflow_storage - assert "workflow2" in workflow_storage - assert isinstance(sensor_config["schedule"], Schedule) - assert sensor_config["cdr_types"] == ["calls", "sms"] - assert sensor_config["workflows"] == [ - WorkflowConfig( - workflow_name="workflow1", - parameters={"dummy_param": 123}, - earliest_date=datetime.date(2016, 1, 1), - date_stencil=DateStencil( - [[datetime.date(2016, 1, 1), datetime.date(2016, 1, 3)], -1, 0] - ), - ), - WorkflowConfig(workflow_name="workflow2"), - ] - - -def test_parse_workflows_yaml_missing_workflows(tmp_path): - """ - Test that parse_workflows_yaml raises a ValueError if the input file - doesn't have a 'workflows' key. - """ - (tmp_path / "dummy_input.yml").write_text( - dedent( - """\ - available_dates_sensor: - schedule: "0 0 * * *" - workflows: - - workflow_name: workflow1 - """ - ) - ) - with pytest.raises( - ValueError, match="Input file does not have a 'workflows' section." - ): - workflow_storage, sensor_config = parse_workflows_yaml( - filename="dummy_input.yml", - inputs_dir=str(tmp_path), - workflow_storage_dir=str(tmp_path / ".prefect/flows"), - ) - - -def test_parse_workflows_yaml_missing_available_dates_sensor(tmp_path): - """ - Test that parse_workflows_yaml raises a ValueError if the input file - doesn't have a 'workflows' key. - """ - (tmp_path / "notebook1.ipynb").touch() - (tmp_path / "dummy_input.yml").write_text( - dedent( - """\ - workflows: - - name: workflow1 - notebooks: - notebook1: - filename: notebook1.ipynb - """ - ) - ) - with pytest.raises( - ValueError, - match="Input file does not have an 'available_dates_sensor' section.", - ): - workflow_storage, sensor_config = parse_workflows_yaml( - filename="dummy_input.yml", - inputs_dir=str(tmp_path), - workflow_storage_dir=str(tmp_path / ".prefect/flows"), - ) diff --git a/autoflow/tests/test_sensor.py b/autoflow/tests/test_sensor.py deleted file mode 100644 index 5016acee4e..0000000000 --- a/autoflow/tests/test_sensor.py +++ /dev/null @@ -1,660 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -from unittest.mock import call, create_autospec, Mock - -import pendulum -import prefect -from prefect.core import Edge -from prefect.engine import TaskRunner -from prefect.engine.state import Failed, Success -from prefect.storage import Local -from prefect.schedules import CronSchedule -from prefect.tasks.core.function import FunctionTask -from prefect.utilities.configuration import set_temporary_config - -from autoflow.date_stencil import DateStencil -from autoflow.model import RunState -from autoflow.sensor import ( - available_dates_sensor, - filter_dates, - get_available_dates, - get_parametrised_workflows, - record_workflow_run_state, - run_workflow, - skip_if_already_run, - WorkflowConfig, -) - - -def test_workflow_config_defaults(): - """ - Test that WorkflowConfig attributes 'parameters', 'earliest_date' and 'date_stencil' have the correct defaults. - """ - workflow_config = WorkflowConfig(workflow_name="DUMMY_FLOW") - assert workflow_config.parameters is None - assert workflow_config.earliest_date is None - assert workflow_config.date_stencil._intervals == ((0, 1),) - - -def test_get_available_dates(monkeypatch, test_logger): - """ - Test that get_available_dates gets available dates using FlowClient and returns as date objects in a sorted list. - """ - flowclient_available_dates = { - "cdr_type_1": ["2016-01-01", "2016-01-03"], - "cdr_type_2": ["2016-01-01", "2016-01-02"], - } - connect_mock = Mock() - monkeypatch.setattr("flowclient.connect", connect_mock) - monkeypatch.setattr( - "flowclient.get_available_dates", lambda connection: flowclient_available_dates - ) - monkeypatch.setenv("FLOWAPI_TOKEN", "DUMMY_TOKEN") - with set_temporary_config({"flowapi_url": "DUMMY_URL"}), prefect.context( - logger=test_logger - ): - dates = get_available_dates.run() - connect_mock.assert_called_once_with( - ssl_certificate=None, url="DUMMY_URL", token="DUMMY_TOKEN" - ) - assert dates == [ - pendulum.date(2016, 1, 1), - pendulum.date(2016, 1, 2), - pendulum.date(2016, 1, 3), - ] - - -def test_get_available_dates_ssl_certificate(monkeypatch, test_logger): - flowclient_available_dates = { - "cdr_type_1": ["2016-01-01", "2016-01-03"], - "cdr_type_2": ["2016-01-01", "2016-01-02"], - } - connect_mock = Mock() - monkeypatch.setattr("flowclient.connect", connect_mock) - monkeypatch.setattr( - "flowclient.get_available_dates", lambda connection: flowclient_available_dates - ) - monkeypatch.setenv("FLOWAPI_TOKEN", "DUMMY_TOKEN") - monkeypatch.setenv("SSL_CERTIFICATE_FILE", "DUMMY_SSL_CERT") - with set_temporary_config({"flowapi_url": "DUMMY_URL"}), prefect.context( - logger=test_logger - ): - get_available_dates.run() - connect_mock.assert_called_once_with( - url="DUMMY_URL", token="DUMMY_TOKEN", ssl_certificate="DUMMY_SSL_CERT" - ) - - -def test_get_available_dates_cdr_types(monkeypatch, test_logger): - """ - Test that get_available_dates can get available dates for a subset of CDR types. - """ - flowclient_available_dates = { - "cdr_type_1": ["2016-01-01", "2016-01-03"], - "cdr_type_2": ["2016-01-01", "2016-01-02"], - "cdr_type_3": ["2016-01-03", "2016-01-04"], - } - monkeypatch.setattr("flowclient.connect", Mock()) - monkeypatch.setattr( - "flowclient.get_available_dates", lambda connection: flowclient_available_dates - ) - monkeypatch.setenv("FLOWAPI_TOKEN", "DUMMY_TOKEN") - with set_temporary_config({"flowapi_url": "DUMMY_URL"}), prefect.context( - logger=test_logger - ): - dates = get_available_dates.run(cdr_types=["cdr_type_1", "cdr_type_2"]) - assert dates == [ - pendulum.date(2016, 1, 1), - pendulum.date(2016, 1, 2), - pendulum.date(2016, 1, 3), - ] - - -def test_get_available_dates_warns(monkeypatch, test_logger): - """ - Test that get_available_dates warns if no dates are available for a specified CDR type, - but still returns the available dates for other specified CDR types. - """ - flowclient_available_dates = { - "cdr_type_1": ["2016-01-01", "2016-01-03"], - "cdr_type_2": ["2016-01-01", "2016-01-02"], - } - monkeypatch.setattr("flowclient.connect", Mock()) - monkeypatch.setattr( - "flowclient.get_available_dates", lambda connection: flowclient_available_dates - ) - monkeypatch.setenv("FLOWAPI_TOKEN", "DUMMY_TOKEN") - with set_temporary_config({"flowapi_url": "DUMMY_URL"}), prefect.context( - logger=test_logger - ), pytest.warns( - UserWarning, match="No data available for CDR types {'cdr_type_3'}." - ): - dates = get_available_dates.run(cdr_types=["cdr_type_1", "cdr_type_3"]) - assert dates == [pendulum.date(2016, 1, 1), pendulum.date(2016, 1, 3)] - - -def test_filter_dates_no_filter(test_logger): - """ - Test that the filter_dates task does not filter dates if neither earliest_date nor date_stencil are set. - """ - dates = list( - pendulum.period(pendulum.date(2016, 1, 1), pendulum.date(2016, 1, 7)).range( - "days" - ) - ) - workflow_config = WorkflowConfig(workflow_name="DUMMY_FLOW") - with prefect.context(logger=test_logger): - filtered_dates = filter_dates.run( - available_dates=dates, workflow_config=workflow_config - ) - assert not filtered_dates is dates - assert filtered_dates == dates - - -def test_filter_dates_by_earliest_date(test_logger): - """ - Test that the filter_dates task removes dates before workflow_config.earliest_date. - """ - dates = list( - pendulum.period(pendulum.date(2016, 1, 1), pendulum.date(2016, 1, 7)).range( - "days" - ) - ) - workflow_config = WorkflowConfig( - workflow_name="DUMMY_FLOW", earliest_date=pendulum.date(2016, 1, 4) - ) - with prefect.context(logger=test_logger): - filtered_dates = filter_dates.run( - available_dates=dates, workflow_config=workflow_config - ) - assert filtered_dates == list( - pendulum.period(pendulum.date(2016, 1, 4), pendulum.date(2016, 1, 7)).range( - "days" - ) - ) - - -def test_filter_dates_by_stencil(test_logger): - """ - Test that filter_dates removes dates for which workflow_config.date_stencil is not available. - """ - dates = [pendulum.date(2016, 1, d) for d in [1, 3, 4, 5, 6]] - workflow_config = WorkflowConfig( - workflow_name="DUMMY_FLOW", - earliest_date=None, - date_stencil=DateStencil([-2, 0]), - ) - with prefect.context(logger=test_logger): - filtered_dates = filter_dates.run( - available_dates=dates, workflow_config=workflow_config - ) - assert filtered_dates == [pendulum.date(2016, 1, d) for d in [3, 5, 6]] - - -def test_filter_dates_by_earliest_and_stencil(test_logger): - """ - Test that filter_dates filters using both earliest_date and date_stencil, if both are provided. - """ - dates = [pendulum.date(2016, 1, d) for d in [1, 3, 4, 5, 6]] - workflow_config = WorkflowConfig( - workflow_name="DUMMY_FLOW", - earliest_date=pendulum.date(2016, 1, 4), - date_stencil=DateStencil([-2, 0]), - ) - with prefect.context(logger=test_logger): - filtered_dates = filter_dates.run( - available_dates=dates, workflow_config=workflow_config - ) - assert filtered_dates == [pendulum.date(2016, 1, d) for d in [5, 6]] - - -def test_get_parametrised_workflows(test_logger, tmpdir): - """ - Test that get_parametrised_workflows correctly combines workflow parameters with dates. - """ - workflow_storage = Local(tmpdir) - workflow_storage.add_flow(prefect.Flow(name="WORKFLOW_1")) - workflow_storage.add_flow(prefect.Flow(name="WORKFLOW_2")) - - workflow_configs = [ - WorkflowConfig(workflow_name="WORKFLOW_1"), - WorkflowConfig( - workflow_name="WORKFLOW_2", - parameters={"DUMMY_PARAM": "DUMMY_VALUE"}, - date_stencil=DateStencil([-1, 0]), - ), - ] - lists_of_dates = [ - [ - pendulum.date(2016, 1, 1), - pendulum.date(2016, 1, 2), - pendulum.date(2016, 1, 3), - ], - [pendulum.date(2016, 1, 4), pendulum.date(2016, 1, 5)], - ] - - with prefect.context(logger=test_logger): - parametrised_workflows = get_parametrised_workflows.run( - workflow_configs=workflow_configs, - lists_of_dates=lists_of_dates, - workflow_storage=workflow_storage, - ) - - assert len(parametrised_workflows) == 5 - for i, refdate in enumerate(lists_of_dates[0]): - assert parametrised_workflows[i][0].name == "WORKFLOW_1" - assert parametrised_workflows[i][1] == { - "reference_date": refdate, - "date_ranges": [(refdate, refdate.add(days=1))], - } - for i in [3, 4]: - assert parametrised_workflows[i][0].name == "WORKFLOW_2" - assert parametrised_workflows[i][1] == { - "DUMMY_PARAM": "DUMMY_VALUE", - "reference_date": pendulum.date(2016, 1, i + 1), - "date_ranges": [ - (pendulum.date(2016, 1, i), pendulum.date(2016, 1, i + 1)), - (pendulum.date(2016, 1, i + 1), pendulum.date(2016, 1, i + 2)), - ], - } - - -@pytest.mark.parametrize( - "state,is_skipped", - [ - (RunState.running, True), - (RunState.success, True), - (RunState.failed, False), - (None, False), - ], -) -def test_skip_if_already_run(monkeypatch, test_logger, state, is_skipped): - """ - Test that the skip_if_already_run task skips if the workflow's most recent - state is 'running' or 'success', and does not skip if the state is - None (i.e. not run before) or 'failed'. - """ - get_session_mock = Mock() - get_most_recent_state_mock = Mock(return_value=state) - monkeypatch.setattr("autoflow.utils.get_session", get_session_mock) - monkeypatch.setattr( - "autoflow.sensor.WorkflowRuns.get_most_recent_state", get_most_recent_state_mock - ) - - runner = TaskRunner(task=skip_if_already_run) - upstream_edge = Edge( - prefect.Task(), skip_if_already_run, key="parametrised_workflow" - ) - with set_temporary_config({"db_uri": "DUMMY_DB_URI"}): - task_state = runner.run( - upstream_states={ - upstream_edge: Success( - result=( - prefect.Flow(name="DUMMY_WORFLOW_NAME"), - {"DUMMY_PARAM": "DUMMY_VALUE"}, - ) - ) - }, - context=dict(logger=test_logger), - ) - - get_session_mock.assert_called_once_with("DUMMY_DB_URI") - get_most_recent_state_mock.assert_called_once_with( - workflow_name="DUMMY_WORFLOW_NAME", - parameters={"DUMMY_PARAM": "DUMMY_VALUE"}, - session=get_session_mock.return_value, - ) - assert task_state.is_successful() - assert is_skipped == task_state.is_skipped() - - -def test_skip_if_already_run_unrecognised_state(monkeypatch, test_logger): - """ - Test that skip_if_already_run raises a ValueError if get_most_recent_state - returns an unrecognised state. - """ - monkeypatch.setattr("autoflow.utils.get_session", Mock()) - monkeypatch.setattr( - "autoflow.sensor.WorkflowRuns.get_most_recent_state", - Mock(return_value="BAD_STATE"), - ) - - with set_temporary_config({"db_uri": "DUMMY_DB_URI"}), prefect.context( - logger=test_logger - ), pytest.raises(ValueError, match="Unrecognised workflow state: 'BAD_STATE'"): - skip_if_already_run.run( - (prefect.Flow(name="DUMMY_WORFLOW_NAME"), {"DUMMY_PARAM": "DUMMY_VALUE"}) - ) - - -def test_record_workflow_run_state(monkeypatch, test_logger): - """ - Test that the record_workflow_run_state task calls WorkflowRuns.set_state with the correct arguments. - """ - get_session_mock = Mock() - set_state_mock = Mock() - monkeypatch.setattr("autoflow.utils.get_session", get_session_mock) - monkeypatch.setattr("autoflow.sensor.WorkflowRuns.set_state", set_state_mock) - dummy_parametrised_workflow = ( - prefect.Flow(name="DUMMY_FLOW"), - {"DUMMY_PARAM": "DUMMY_VALUE"}, - ) - with set_temporary_config({"db_uri": "DUMMY_DB_URI"}), prefect.context( - logger=test_logger - ): - record_workflow_run_state.run( - parametrised_workflow=dummy_parametrised_workflow, state=RunState.success - ) - get_session_mock.assert_called_once_with("DUMMY_DB_URI") - set_state_mock.assert_called_once_with( - workflow_name="DUMMY_FLOW", - parameters={"DUMMY_PARAM": "DUMMY_VALUE"}, - state=RunState.success, - session=get_session_mock.return_value, - ) - - -def test_run_workflow(test_logger): - """ - Test that the run_workflow task runs a workflow with the given parameters. - """ - function_mock = create_autospec(lambda dummy_param: None) - - with prefect.Flow("Dummy workflow") as dummy_workflow: - dummy_param = prefect.Parameter("dummy_param") - FunctionTask(function_mock)(dummy_param=dummy_param) - - runner = TaskRunner(task=run_workflow) - upstream_edge = Edge(prefect.Task(), run_workflow, key="parametrised_workflow") - task_state = runner.run( - upstream_states={ - upstream_edge: Success( - result=(dummy_workflow, dict(dummy_param="DUMMY_VALUE")) - ) - }, - context=dict(logger=test_logger), - ) - assert task_state.is_successful() - function_mock.assert_called_once_with(dummy_param="DUMMY_VALUE") - - -def test_run_workflow_fails(test_logger): - """ - Test that the run_workflow task fails if the workflow fails. - """ - function_mock = create_autospec( - lambda dummy_param: None, side_effect=Exception("Workflow failed") - ) - - with prefect.Flow("Dummy workflow") as dummy_workflow: - dummy_param = prefect.Parameter("dummy_param") - FunctionTask(function_mock)(dummy_param=dummy_param) - - runner = TaskRunner(task=run_workflow) - upstream_edge = Edge(prefect.Task(), run_workflow, key="parametrised_workflow") - task_state = runner.run( - upstream_states={ - upstream_edge: Success( - result=(dummy_workflow, dict(dummy_param="DUMMY_VALUE")) - ) - }, - context=dict(logger=test_logger), - ) - assert task_state.is_failed() - - -def test_run_workflow_ignores_schedule(test_logger): - """ - Test that run_workflow ignores the workflow's schedule. - """ - function_mock = create_autospec(lambda dummy_param: None) - # Flow with no more scheduled runs - with prefect.Flow( - "Dummy_workflow", - schedule=CronSchedule("0 0 * * *", end_date=pendulum.now().subtract(days=2)), - ) as dummy_workflow: - dummy_param = prefect.Parameter("dummy_param") - FunctionTask(function_mock)(dummy_param=dummy_param) - - with prefect.context(logger=test_logger): - run_workflow.run( - parametrised_workflow=(dummy_workflow, dict(dummy_param="DUMMY_VALUE")) - ) - function_mock.assert_called_once_with(dummy_param="DUMMY_VALUE") - - -def test_available_dates_sensor(monkeypatch, postgres_test_db, tmpdir): - """ - Test that the available_dates_sensor flow runs the specified workflows with - the correct parameters, and does not run successful workflow runs more than - once for the same date. - """ - # Mock flowclient - flowclient_available_dates = { - "cdr_type_1": ["2016-01-01", "2016-01-02", "2016-01-03", "2016-01-04"], - "cdr_type_2": ["2016-01-04", "2016-01-05", "2016-01-06", "2016-01-07"], - "cdr_type_3": ["2016-01-08"], - } - monkeypatch.setattr( - "flowclient.get_available_dates", lambda connection: flowclient_available_dates - ) - monkeypatch.setattr("flowclient.connect", Mock()) - monkeypatch.setenv("FLOWAPI_TOKEN", "DUMMY_TOKEN") - - # Mock workflows - workflow_1 = Mock() - workflow_1.name = "WORKFLOW_1" - workflow_1.run.return_value = Success() - workflow_2 = Mock() - workflow_2.name = "WORKFLOW_2" - workflow_2.run.return_value = Success() - workflow_storage = Local(tmpdir) - workflow_storage.add_flow(workflow_1) - workflow_storage.add_flow(workflow_2) - workflow_storage.get_flow = ( - lambda x: workflow_1 if x == "WORKFLOW_1" else workflow_2 - ) # Replacing get_flow to allow us to return mocks here because otherwise we lose the reference when it reloads from the filesystem - - workflow_configs = [ - WorkflowConfig( - workflow_name="WORKFLOW_1", - parameters={"DUMMY_PARAM_1": "DUMMY_VALUE_1"}, - earliest_date=pendulum.date(2016, 1, 4), - ), - WorkflowConfig( - workflow_name="WORKFLOW_2", - parameters={"DUMMY_PARAM_2": "DUMMY_VALUE_2"}, - date_stencil=DateStencil([[pendulum.date(2016, 1, 3), -1], -1, 0]), - ), - ] - - # Run available dates sensor - with set_temporary_config( - {"flowapi_url": "DUMMY_URL", "db_uri": postgres_test_db.url()} - ): - flow_state = available_dates_sensor.run( - cdr_types=["cdr_type_1", "cdr_type_2"], - workflow_configs=workflow_configs, - workflow_storage=workflow_storage, - ) - - # Check that run was successful and workflows were run with the correct parameters - assert flow_state.is_successful - workflow_1.run.assert_has_calls( - [ - call( - parameters=dict( - reference_date=d, - date_ranges=[(d, d.add(days=1))], - DUMMY_PARAM_1="DUMMY_VALUE_1", - ), - run_on_schedule=False, - ) - for d in pendulum.period( - pendulum.date(2016, 1, 4), pendulum.date(2016, 1, 7) - ) - ] - ) - workflow_2.run.assert_has_calls( - [ - call( - parameters=dict( - reference_date=d, - date_ranges=[ - (pendulum.date(2016, 1, 3), d.subtract(days=1)), - (d.subtract(days=1), d), - (d, d.add(days=1)), - ], - DUMMY_PARAM_2="DUMMY_VALUE_2", - ), - run_on_schedule=False, - ) - for d in pendulum.period( - pendulum.date(2016, 1, 5), pendulum.date(2016, 1, 7) - ) - ] - ) - - # Reset workflow mocks - workflow_1.reset_mock() - workflow_2.reset_mock() - - # Add more available dates - flowclient_available_dates = { - "cdr_type_1": ["2016-01-01", "2016-01-02", "2016-01-03", "2016-01-04"], - "cdr_type_2": [ - "2016-01-04", - "2016-01-05", - "2016-01-06", - "2016-01-07", - "2016-01-08", - ], - "cdr_type_3": ["2016-01-08"], - } - monkeypatch.setattr( - "flowclient.get_available_dates", lambda connection: flowclient_available_dates - ) - - # Run available dates sensor again - with set_temporary_config( - {"flowapi_url": "DUMMY_URL", "db_uri": postgres_test_db.url()} - ): - flow_state = available_dates_sensor.run( - cdr_types=["cdr_type_1", "cdr_type_2"], - workflow_configs=workflow_configs, - workflow_storage=workflow_storage, - ) - - # Check that workflows only ran for the new date - workflow_1.run.assert_called_once_with( - parameters=dict( - reference_date=pendulum.date(2016, 1, 8), - date_ranges=[(pendulum.date(2016, 1, 8), pendulum.date(2016, 1, 9))], - DUMMY_PARAM_1="DUMMY_VALUE_1", - ), - run_on_schedule=False, - ) - workflow_2.run.assert_called_once_with( - parameters=dict( - reference_date=pendulum.date(2016, 1, 8), - date_ranges=[ - (pendulum.date(2016, 1, 3), pendulum.date(2016, 1, 7)), - (pendulum.date(2016, 1, 7), pendulum.date(2016, 1, 8)), - (pendulum.date(2016, 1, 8), pendulum.date(2016, 1, 9)), - ], - DUMMY_PARAM_2="DUMMY_VALUE_2", - ), - run_on_schedule=False, - ) - - -def test_available_dates_sensor_retries(monkeypatch, postgres_test_db, tmpdir): - """ - Test that the available_dates_sensor flow re-runs workflows that failed on - the previous attempt, and does not re-run them again once they have succeeded. - """ - # Mock flowclient - flowclient_available_dates = { - "dummy_cdr_type": ["2016-01-01", "2016-01-02", "2016-01-03"] - } - monkeypatch.setattr( - "flowclient.get_available_dates", lambda connection: flowclient_available_dates - ) - monkeypatch.setattr("flowclient.connect", Mock()) - monkeypatch.setenv("FLOWAPI_TOKEN", "DUMMY_TOKEN") - - # Mock workflows - dummy_workflow = Mock() - dummy_workflow.name = "DUMMY_WORKFLOW" - dummy_workflow.run.side_effect = [Failed(), Success(), Success()] - workflow_storage = Local(tmpdir) - workflow_storage.add_flow(dummy_workflow) - workflow_storage.get_flow = ( - lambda x: dummy_workflow - ) # Replacing get_flow to allow us to return a mock here because otherwise we lose the reference when it reloads from the filesystem - - workflow_configs = [WorkflowConfig(workflow_name="DUMMY_WORKFLOW")] - - # Run available dates sensor - with set_temporary_config( - {"flowapi_url": "DUMMY_URL", "db_uri": postgres_test_db.url()} - ): - flow_state = available_dates_sensor.run( - workflow_configs=workflow_configs, workflow_storage=workflow_storage - ) - - # Check that sensor flow ended in a 'failed' state, and dummy_workflow.run() was called 3 times - assert flow_state.is_failed - dummy_workflow.run.assert_has_calls( - [ - call( - parameters=dict(reference_date=d, date_ranges=[(d, d.add(days=1))]), - run_on_schedule=False, - ) - for d in pendulum.period( - pendulum.date(2016, 1, 1), pendulum.date(2016, 1, 3) - ) - ] - ) - - # Reset workflow mock - dummy_workflow.reset_mock() - dummy_workflow.run.side_effect = None - dummy_workflow.run.return_value = Success() - - # Run available dates sensor again - with set_temporary_config( - {"flowapi_url": "DUMMY_URL", "db_uri": postgres_test_db.url()} - ): - flow_state = available_dates_sensor.run( - workflow_configs=workflow_configs, workflow_storage=workflow_storage - ) - - # Check that sensor flow was successful, and dummy_workflow only re-ran for the date for which it previously failed - assert flow_state.is_successful - dummy_workflow.run.assert_called_once_with( - parameters=dict( - reference_date=pendulum.date(2016, 1, 1), - date_ranges=[(pendulum.date(2016, 1, 1), pendulum.date(2016, 1, 2))], - ), - run_on_schedule=False, - ) - - # Reset workflow mock again - dummy_workflow.reset_mock() - - # Run available dates sensor once more - with set_temporary_config( - {"flowapi_url": "DUMMY_URL", "db_uri": postgres_test_db.url()} - ): - flow_state = available_dates_sensor.run( - workflow_configs=workflow_configs, workflow_storage=workflow_storage - ) - - # Check that dummy_workflow did not run again, now that it has run successfully - assert flow_state.is_successful - dummy_workflow.run.assert_not_called() diff --git a/autoflow/tests/test_utils.py b/autoflow/tests/test_utils.py deleted file mode 100644 index 0be255b4df..0000000000 --- a/autoflow/tests/test_utils.py +++ /dev/null @@ -1,331 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -import datetime -import json -from collections import OrderedDict -from pathlib import Path -from unittest.mock import Mock, patch, mock_open - -import pendulum - -from autoflow.utils import * - - -def test_get_output_filename(): - """ - Test that get_output_filename returns the expected filename when a tag is provided. - """ - now = pendulum.parse("2016-01-01") - with patch("pendulum.now", lambda x: now): - output_filename = get_output_filename("dummy_filename.suffix", "DUMMY_TAG") - assert output_filename == "dummy_filename__DUMMY_TAG__20160101T000000Z.suffix" - - -def test_get_output_filename_without_tag(): - """ - Test that get_output_filename returns the expected filename when a tag is not provided. - """ - now = pendulum.parse("2016-01-01") - with patch("pendulum.now", lambda x: now): - output_filename = get_output_filename("dummy_filename.suffix") - assert output_filename == "dummy_filename__20160101T000000Z.suffix" - - -def test_get_different_params_hash_for_different_parameters(): - """ - Test that get_params_hash gives different results for different parameters. - """ - hash1 = get_params_hash({"DUMMY_PARAM_1": 1}) - hash2 = get_params_hash({"DUMMY_PARAM_2": 2}) - assert hash1 != hash2 - - -def test_params_hash_independent_of_order(): - """ - Test that get_params_hash gives the same result if order of parameters changes. - """ - hash1 = get_params_hash({"DUMMY_PARAM_1": 1, "DUMMY_PARAM_2": 2}) - hash2 = get_params_hash({"DUMMY_PARAM_2": 2, "DUMMY_PARAM_1": 1}) - assert hash1 == hash2 - - -def test_get_params_hash_can_handle_dates(): - """ - Test that get_params_hash returns a result if the parameters contain dates, - and that different dates produce different hashes. - """ - hash1 = get_params_hash({"DUMMY_PARAM": pendulum.date(2016, 1, 1)}) - hash2 = get_params_hash({"DUMMY_PARAM": pendulum.date(2016, 1, 2)}) - assert hash1 != hash2 - - -def test_get_session(monkeypatch): - """ - Test that get_session returns a session that connects to the correct database. - """ - db_name = "DUMMY_NAME" - host = "DUMMY_HOST" - password = "DUMMY_PASSWORD" - port = 6666 - user = "DUMMY_USER" - - monkeypatch.setenv("AUTOFLOW_DB_PASSWORD", password) - mock_psycopg2_connect = Mock() - monkeypatch.setattr("psycopg2.connect", mock_psycopg2_connect) - - s = get_session(f"postgresql://{user}:{{}}@{host}:{port}/{db_name}") - - try: - s.connection() - except TypeError: - # we get an exception because not a real - # connection catching and ignoring - pass - - mock_psycopg2_connect.assert_called_once_with( - database=db_name, host=host, password=password, port=port, user=user - ) - - -def test_get_session_without_password(monkeypatch): - """ - Test that get_session works for a passwordless database. - """ - db_name = "DUMMY_NAME" - host = "DUMMY_HOST" - port = 6666 - user = "DUMMY_USER" - - mock_psycopg2_connect = Mock() - monkeypatch.setattr("psycopg2.connect", mock_psycopg2_connect) - - s = get_session(f"postgresql://{user}@{host}:{port}/{db_name}") - - try: - s.connection() - except TypeError: - # we get an exception because not a real - # connection catching and ignoring - pass - - mock_psycopg2_connect.assert_called_once_with( - database=db_name, host=host, port=port, user=user - ) - - -def test_session_scope(monkeypatch): - """ - Test that session_scope closes the session. - """ - mock_session = Mock() - monkeypatch.setattr("autoflow.utils.get_session", Mock(return_value=mock_session)) - with session_scope("sqlite:///") as session: - pass - assert session is mock_session - mock_session.commit.assert_called_once() - mock_session.close.assert_called_once() - - -def test_session_scope_raises(monkeypatch): - """ - Test that session_scope rolls back the session if an exception is raised, and re-raises the exception. - """ - mock_session = Mock() - monkeypatch.setattr("autoflow.utils.get_session", Mock(return_value=mock_session)) - with pytest.raises(Exception, match="Dummy exception"), session_scope( - "sqlite:///" - ) as session: - raise Exception("Dummy exception") - mock_session.commit.assert_not_called() - mock_session.rollback.assert_called_once() - mock_session.close.assert_called_once() - - -@pytest.mark.parametrize( - "before,expected", - [ - ( - {"key": [1, 2.7, "string", True, None]}, - '{"key": [1, 2.7, "string", true, null]}', - ), - (pendulum.parse("2016-01-01", exact=True), '"2016-01-01"'), - ((False, 1, "2", 3.0), '[false, 1, "2", 3.0]'), - ], -) -def test_make_json_serialisable(before, expected): - """ - Test that make_json_serialisable returns objects that can be serialised - using the default json.dumps(). - """ - after = make_json_serialisable(before) - assert json.dumps(after) == expected - - -def test_get_additional_parameter_names_for_notebooks(): - """ - Test that get_additional_parameter_names_for_notebooks returns the set of - notebook parameters, excluding notebook keys. - """ - notebooks = { - "notebook1": {"parameters": {"p1": "DUMMY_PARAM_1"}}, - "notebook2": { - "parameters": { - "p1": "notebook1", - "p2": "DUMMY_PARAM_1", - "p3": "DUMMY_PARAM_2", - } - }, - } - additional_params = get_additional_parameter_names_for_notebooks(notebooks) - assert additional_params == {"DUMMY_PARAM_1", "DUMMY_PARAM_2"} - - -def test_get_additional_parameter_names_for_notebooks_with_reserved_parameter_names(): - """ - Test that get_additional_parameter_names_for_notebooks excludes reserved - parameter names from the returned set. - """ - notebooks = { - "notebook": {"parameters": {"p1": "DUMMY_PARAM", "p2": "RESERVED_PARAM"}} - } - reserved_parameter_names = {"RESERVED_PARAM"} - additional_params = get_additional_parameter_names_for_notebooks( - notebooks, reserved_parameter_names - ) - assert additional_params == {"DUMMY_PARAM"} - - -def test_get_additional_parameter_names_for_notebooks_with_no_parameters(): - """ - Test that get_additional_parameter_names_for_notebooks works if a notebook - specification has no 'parameters' field. - """ - notebooks = {"notebook": {"filename": "DUMMY_FILENAME"}} - additional_params = get_additional_parameter_names_for_notebooks(notebooks) - assert additional_params == set() - - -def test_sort_notebooks(): - """ - Test that sort_notebooks sorts notebooks into a correct order. - """ - notebooks = { - "notebook1": {"parameters": {"p1": "notebook2"}}, - "notebook2": {"parameters": {}}, - "notebook3": {"parameters": {"p1": "notebook1", "p2": "notebook2"}}, - } - sorted_notebooks = sort_notebooks(notebooks) - assert isinstance(sorted_notebooks, OrderedDict) - assert sorted_notebooks == OrderedDict( - (key, notebooks[key]) for key in ["notebook2", "notebook1", "notebook3"] - ) - - -def test_sort_notebooks_circular_dependency(): - """ - Test that sort_notebooks raises an error if notebooks have circular dependencies. - """ - notebooks = { - "notebook1": {"parameters": {"p1": "notebook2"}}, - "notebook2": {"parameters": {"p1": "notebook1"}}, - } - with pytest.raises( - ValueError, match="Notebook specifications contain circular dependencies." - ): - sorted_notebooks = sort_notebooks(notebooks) - - -def test_sort_notebooks_with_no_parameters(): - """ - Test that sort_notebooks works if a notebook specification has no 'parameters' field. - """ - notebooks = {"notebook1": {"filename": "DUMMY_FILENAME"}} - sorted_notebooks = sort_notebooks(notebooks) - assert isinstance(sorted_notebooks, OrderedDict) - assert sorted_notebooks == OrderedDict(notebooks) - - -def test_notebook_to_asciidoc(monkeypatch): - """ - Test that notebook_to_asciidoc uses a nbconvert.ASCIIDocExporter with the - specified template to convert the notebook. - """ - open_mock = mock_open() - nbformat_read_mock = Mock(return_value="DUMMY_NB") - ExporterMock = Mock() - ExporterMock.return_value.from_notebook_node.return_value = ( - "DUMMY_BODY", - "DUMMY_RESOURCES", - ) - monkeypatch.setattr("builtins.open", open_mock) - monkeypatch.setattr("nbformat.read", nbformat_read_mock) - monkeypatch.setattr("nbconvert.ASCIIDocExporter", ExporterMock) - - body, resources = notebook_to_asciidoc( - notebook_path="DUMMY_NOTEBOOKS_DIR/DUMMY_FILENAME.ipynb", - asciidoc_template_path="DUMMY_TEMPLATE_PATH", - ) - - assert body == "DUMMY_BODY" - assert resources == "DUMMY_RESOURCES" - open_mock.assert_called_once_with("DUMMY_NOTEBOOKS_DIR/DUMMY_FILENAME.ipynb") - nbformat_read_mock.assert_called_once_with(open_mock(), as_version=4) - assert open_mock().__exit__.called_once() or open_mock().close.called_once() - ExporterMock.assert_called_once_with(template_file="DUMMY_TEMPLATE_PATH") - ExporterMock.return_value.from_notebook_node.assert_called_once_with("DUMMY_NB") - - -def test_notebook_to_asciidoc_no_template(monkeypatch): - """ - Test that notebook_to_asciidoc can be called without specifying a template - file, and that the nbconvert default template will be used in this case. - """ - ExporterMock = Mock() - ExporterMock.return_value.from_notebook_node.return_value = ( - "DUMMY_BODY", - "DUMMY_RESOURCES", - ) - monkeypatch.setattr("builtins.open", mock_open()) - monkeypatch.setattr("nbformat.read", Mock()) - monkeypatch.setattr("nbconvert.ASCIIDocExporter", ExporterMock) - - body, resources = notebook_to_asciidoc( - notebook_path="DUMMY_NOTEBOOKS_DIR/DUMMY_FILENAME.ipynb" - ) - - ExporterMock.assert_called_once_with() - - -def test_asciidoc_to_pdf(monkeypatch): - """ - Test that asciidoc_to_pdf writes asciidoc file contents to temporary files, - and runs asciidoctor_pdf on those files. - """ - - def asciidoctor_pdf_mock_side_effect(filename, o): - filepath = Path(filename) - assert filepath.exists() - resource_path = filepath.parent / "DUMMY_RESOURCE_NAME" - assert resource_path.exists() - with open(filepath, "r") as f_body: - assert "DUMMY_BODY" == f_body.read() - with open(resource_path, "rb") as f_res: - assert b"DUMMY_RESOURCE_CONTENT" == f_res.read() - - asciidoctor_pdf_mock = Mock(side_effect=asciidoctor_pdf_mock_side_effect) - monkeypatch.setattr("autoflow.utils.asciidoctor_pdf", asciidoctor_pdf_mock) - - asciidoc_to_pdf( - body="DUMMY_BODY", - resources=dict(outputs={"DUMMY_RESOURCE_NAME": b"DUMMY_RESOURCE_CONTENT"}), - output_path="DUMMY_OUTPUT_PATH", - ) - - asciidoctor_pdf_mock.assert_called_once() - args, kwargs = asciidoctor_pdf_mock.call_args - assert not Path(args[0]).exists() - assert kwargs["o"] == "DUMMY_OUTPUT_PATH" diff --git a/autoflow/tests/test_workflows.py b/autoflow/tests/test_workflows.py deleted file mode 100644 index 5d6254131d..0000000000 --- a/autoflow/tests/test_workflows.py +++ /dev/null @@ -1,336 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -from collections import OrderedDict -from unittest.mock import call, Mock - -import pendulum -import prefect -from prefect.utilities.configuration import set_temporary_config - -from autoflow.workflows import ( - convert_notebook_to_pdf, - get_flowapi_url, - get_tag, - make_notebooks_workflow, - papermill_execute_notebook, -) -from autoflow.utils import get_params_hash - - -def test_get_flowapi_url(): - """ - Test that the get_flowapi_url task returns the FlowAPI URL set in the prefect config. - """ - with set_temporary_config({"flowapi_url": "DUMMY_URL"}): - flowapi_url = get_flowapi_url.run() - assert flowapi_url == "DUMMY_URL" - - -def test_get_tag(): - """ - Test that the get_tag task returns the expected tag. - """ - dummy_params = {"DUMMY_PARAM_NAME": "DUMMY_PARAM_VALUE"} - with prefect.context(parameters=dummy_params, flow_name="DUMMY-FLOW-NAME"): - tag = get_tag.run() - assert tag == f"DUMMY-FLOW-NAME_{get_params_hash(dummy_params)}" - - -def test_get_tag_with_date(): - """ - Test that the get_tag task returns the expected tag when a reference date is provided. - """ - dummy_params = {"DUMMY_PARAM_NAME": "DUMMY_PARAM_VALUE"} - with prefect.context(parameters=dummy_params, flow_name="DUMMY-FLOW-NAME"): - tag = get_tag.run(reference_date=pendulum.date(2016, 1, 1)) - assert tag == f"DUMMY-FLOW-NAME_2016-01-01_{get_params_hash(dummy_params)}" - - -def test_papermill_execute_notebook(monkeypatch, test_logger): - """ - Test that the papermill_execute_notebook task calls papermill.execute_notebook - with the correct arguments, and returns the output filename. - """ - execute_notebook_mock = Mock() - get_output_filename_mock = Mock(return_value="DUMMY_OUTPUT_FILENAME") - monkeypatch.setattr( - "autoflow.workflows.make_json_serialisable", - lambda x: {k: f"SAFE_{v}" for k, v in x.items()}, - ) - monkeypatch.setattr( - "autoflow.workflows.get_output_filename", get_output_filename_mock - ) - monkeypatch.setattr("papermill.execute_notebook", execute_notebook_mock) - - with set_temporary_config( - { - "inputs.inputs_dir": "DUMMY_INPUTS_DIR", - "outputs.notebooks_dir": "DUMMY_NOTEBOOKS_DIR", - } - ), prefect.context(logger=test_logger): - output_path = papermill_execute_notebook.run( - input_filename="DUMMY_INPUT_FILENAME", - output_tag="DUMMY_TAG", - parameters={"DUMMY_PARAM": "DUMMY_VALUE"}, - dummy_kwarg="DUMMY_KWARG_VALUE", - ) - - assert output_path == "DUMMY_NOTEBOOKS_DIR/DUMMY_OUTPUT_FILENAME" - get_output_filename_mock.assert_called_once_with( - input_filename="DUMMY_INPUT_FILENAME", tag="DUMMY_TAG" - ) - execute_notebook_mock.assert_called_once_with( - "DUMMY_INPUTS_DIR/DUMMY_INPUT_FILENAME", - "DUMMY_NOTEBOOKS_DIR/DUMMY_OUTPUT_FILENAME", - parameters={"DUMMY_PARAM": "SAFE_DUMMY_VALUE"}, - dummy_kwarg="DUMMY_KWARG_VALUE", - ) - - -def test_convert_notebook_to_pdf(monkeypatch, test_logger): - """ - Test that the convert_notebook_to_pdf task calls notebook_to_asciidoc - followed by asciidoc_to_pdf, with the correct arguments. - """ - notebook_to_asciidoc_mock = Mock(return_value=("DUMMY_BODY", "DUMMY_RESOURCES")) - asciidoc_to_pdf_mock = Mock() - monkeypatch.setattr( - "autoflow.workflows.notebook_to_asciidoc", notebook_to_asciidoc_mock - ) - monkeypatch.setattr("autoflow.workflows.asciidoc_to_pdf", asciidoc_to_pdf_mock) - - with set_temporary_config( - { - "asciidoc_template_path": "DUMMY_TEMPLATE_PATH", - "outputs.reports_dir": "DUMMY_REPORTS_DIR", - } - ), prefect.context(logger=test_logger): - output_path = convert_notebook_to_pdf.run( - notebook_path="DUMMY_NOTEBOOKS_DIR/DUMMY_FILENAME.ipynb", - output_filename="DUMMY_OUTPUT_FILENAME", - ) - - assert output_path == "DUMMY_REPORTS_DIR/DUMMY_OUTPUT_FILENAME" - notebook_to_asciidoc_mock.assert_called_once_with( - "DUMMY_NOTEBOOKS_DIR/DUMMY_FILENAME.ipynb", "DUMMY_TEMPLATE_PATH" - ) - asciidoc_to_pdf_mock.assert_called_once_with( - "DUMMY_BODY", "DUMMY_RESOURCES", "DUMMY_REPORTS_DIR/DUMMY_OUTPUT_FILENAME" - ) - - -def test_convert_notebook_to_pdf_default_output_filename(monkeypatch, test_logger): - """ - Test that if the output filename is not specified, the convert_notebook_to_pdf - task will use the stem from the input filename, with extension changed to '.pdf'. - """ - asciidoc_to_pdf_mock = Mock() - monkeypatch.setattr( - "autoflow.workflows.notebook_to_asciidoc", - Mock(return_value=("DUMMY_BODY", "DUMMY_RESOURCES")), - ) - monkeypatch.setattr("autoflow.workflows.asciidoc_to_pdf", asciidoc_to_pdf_mock) - - with set_temporary_config( - { - "asciidoc_template_path": "DUMMY_TEMPLATE_PATH", - "outputs.reports_dir": "DUMMY_REPORTS_DIR", - } - ), prefect.context(logger=test_logger): - output_path = convert_notebook_to_pdf.run( - notebook_path="DUMMY_NOTEBOOKS_DIR/DUMMY_FILENAME.ipynb" - ) - - assert output_path == "DUMMY_REPORTS_DIR/DUMMY_FILENAME.pdf" - asciidoc_to_pdf_mock.assert_called_once_with( - "DUMMY_BODY", "DUMMY_RESOURCES", "DUMMY_REPORTS_DIR/DUMMY_FILENAME.pdf" - ) - - -def test_convert_notebook_to_pdf_custom_template(monkeypatch, test_logger): - """ - Test that if a custom asciidoc template filename is passed to the convert_notebook_to_pdf - task, that template will be used instead of the default. - """ - notebook_to_asciidoc_mock = Mock(return_value=("DUMMY_BODY", "DUMMY_RESOURCES")) - monkeypatch.setattr( - "autoflow.workflows.notebook_to_asciidoc", notebook_to_asciidoc_mock - ) - monkeypatch.setattr("autoflow.workflows.asciidoc_to_pdf", Mock()) - - with set_temporary_config( - { - "asciidoc_template_path": "DEFAULT_TEMPLATE_PATH", - "inputs.inputs_dir": "DUMMY_INPUTS_DIR", - "outputs.reports_dir": "DUMMY_REPORTS_DIR", - } - ), prefect.context(logger=test_logger): - output_path = convert_notebook_to_pdf.run( - notebook_path="DUMMY_NOTEBOOKS_DIR/DUMMY_FILENAME.ipynb", - asciidoc_template="CUSTOM_TEMPLATE_FILENAME", - ) - - notebook_to_asciidoc_mock.assert_called_once_with( - "DUMMY_NOTEBOOKS_DIR/DUMMY_FILENAME.ipynb", - "DUMMY_INPUTS_DIR/CUSTOM_TEMPLATE_FILENAME", - ) - - -def test_convert_notebook_to_pdf_no_template(monkeypatch, test_logger): - """ - Test that if no asciidoc template filename is passed to the convert_notebook_to_pdf - task, and no default template is set in the config, no template will be passed to notebook_to_asciidoc. - """ - notebook_to_asciidoc_mock = Mock(return_value=("DUMMY_BODY", "DUMMY_RESOURCES")) - monkeypatch.setattr( - "autoflow.workflows.notebook_to_asciidoc", notebook_to_asciidoc_mock - ) - monkeypatch.setattr("autoflow.workflows.asciidoc_to_pdf", Mock()) - - with set_temporary_config( - {"outputs.reports_dir": "DUMMY_REPORTS_DIR"} - ), prefect.context(logger=test_logger): - prefect.config.pop("asciidoc_template_path", None) - output_path = convert_notebook_to_pdf.run( - notebook_path="DUMMY_NOTEBOOKS_DIR/DUMMY_FILENAME.ipynb" - ) - - notebook_to_asciidoc_mock.assert_called_once_with( - "DUMMY_NOTEBOOKS_DIR/DUMMY_FILENAME.ipynb", None - ) - - -def test_make_notebooks_workflow(monkeypatch): - """ - Test that make_notebooks_workflow returns a workflow with the correct name - and parameters, and the workflow can run successfully and executes the - correct tasks. - """ - # Patch tasks - monkeypatch.setattr( - "autoflow.workflows.get_tag.run", Mock(return_value="DUMMY_TAG") - ) - execute_notebook_mock = Mock( - side_effect=["DUMMY_OUTPUT_1.ipynb", "DUMMY_OUTPUT_2.ipynb"] - ) - monkeypatch.setattr( - "autoflow.workflows.papermill_execute_notebook.run", execute_notebook_mock - ) - convert_to_pdf_mock = Mock(return_value="DUMMY_REPORT.pdf") - monkeypatch.setattr( - "autoflow.workflows.convert_notebook_to_pdf.run", convert_to_pdf_mock - ) - - # Create workflow - notebooks = OrderedDict( - [ - ( - "notebook1", - dict( - filename="DUMMY_NOTEBOOK1.ipynb", - parameters={ - "param1": "reference_date", - "param2": "flowapi_url", - "param3": "DUMMY_PARAM", - }, - ), - ), - ( - "notebook2", - dict( - filename="DUMMY_NOTEBOOK2.ipynb", - parameters={"param1": "date_ranges", "param2": "notebook1"}, - output={"format": "pdf", "template": None}, - ), - ), - ] - ) - dummy_workflow = make_notebooks_workflow(name="DUMMY_WORKFLOW", notebooks=notebooks) - - # Check workflow has correct name and parameters - assert dummy_workflow.name == "DUMMY_WORKFLOW" - workflow_parameter_names = {p.name for p in dummy_workflow.parameters()} - assert workflow_parameter_names == {"reference_date", "date_ranges", "DUMMY_PARAM"} - - # Run workflow - with set_temporary_config({"flowapi_url": "DUMMY_URL"}): - flow_state = dummy_workflow.run( - reference_date=pendulum.date(2016, 1, 1), - date_ranges=[(pendulum.date(2016, 1, 1), pendulum.date(2016, 1, 1))], - DUMMY_PARAM="DUMMY_VALUE", - ) - - # Check workflow succeeded and ran the correct tasks - assert flow_state.is_successful - execute_notebook_mock.assert_has_calls( - [ - call( - input_filename="DUMMY_NOTEBOOK1.ipynb", - output_tag="DUMMY_TAG", - parameters={ - "param1": pendulum.date(2016, 1, 1), - "param2": "DUMMY_URL", - "param3": "DUMMY_VALUE", - }, - ), - call( - input_filename="DUMMY_NOTEBOOK2.ipynb", - output_tag="DUMMY_TAG", - parameters={ - "param1": [(pendulum.date(2016, 1, 1), pendulum.date(2016, 1, 1))], - "param2": "DUMMY_OUTPUT_1.ipynb", - }, - ), - ] - ) - convert_to_pdf_mock.assert_called_once_with( - notebook_path="DUMMY_OUTPUT_2.ipynb", asciidoc_template=None - ) - - -def test_make_notebooks_workflow_default_parameters(): - """ - Test that a workflow returned by make_notebooks_workflow has parameters - 'reference_date' and 'date_ranges', even if these parameters are not used - by the notebooks. - """ - notebooks = OrderedDict([("notebook1", dict(filename="DUMMY_NOTEBOOK1.ipynb"))]) - dummy_workflow = make_notebooks_workflow(name="DUMMY_WORKFLOW", notebooks=notebooks) - workflow_parameter_names = {p.name for p in dummy_workflow.parameters()} - assert workflow_parameter_names == {"reference_date", "date_ranges"} - - -def test_notebooks_workflow_fails(monkeypatch): - """ - Test that a workflow returned by make_notebooks_workflow ends in a failed state - if an exception is raised while executing a notebook. - """ - # Patch tasks - monkeypatch.setattr( - "autoflow.workflows.get_tag.run", Mock(return_value="DUMMY_TAG") - ) - execute_notebook_mock = Mock(side_effect=Exception("This task failed")) - monkeypatch.setattr( - "autoflow.workflows.papermill_execute_notebook.run", execute_notebook_mock - ) - - # Create workflow - notebooks = OrderedDict([("notebook1", dict(filename="DUMMY_NOTEBOOK1.ipynb"))]) - dummy_workflow = make_notebooks_workflow(name="DUMMY_WORKFLOW", notebooks=notebooks) - - # Run workflow - with set_temporary_config({"flowapi_url": "DUMMY_URL"}): - flow_state = dummy_workflow.run( - reference_date=pendulum.date(2016, 1, 1), - date_ranges=[(pendulum.date(2016, 1, 1), pendulum.date(2016, 1, 1))], - ) - - # Check workflow ran teh notebook execution task, and ended in a failed state - assert flow_state.is_failed - execute_notebook_mock.assert_called_once_with( - input_filename="DUMMY_NOTEBOOK1.ipynb", output_tag="DUMMY_TAG", parameters={} - ) diff --git a/autoflow/versioneer.py b/autoflow/versioneer.py deleted file mode 100644 index 3348535533..0000000000 --- a/autoflow/versioneer.py +++ /dev/null @@ -1,2205 +0,0 @@ -# Version: 0.23 - -"""The Versioneer - like a rocketeer, but for versions. - -The Versioneer -============== - -* like a rocketeer, but for versions! -* https://github.com/python-versioneer/python-versioneer -* Brian Warner -* License: Public Domain (CC0-1.0) -* Compatible with: Python 3.7, 3.8, 3.9, 3.10 and pypy3 -* [![Latest Version][pypi-image]][pypi-url] -* [![Build Status][travis-image]][travis-url] - -This is a tool for managing a recorded version number in distutils/setuptools-based -python projects. The goal is to remove the tedious and error-prone "update -the embedded version string" step from your release process. Making a new -release should be as easy as recording a new tag in your version-control -system, and maybe making new tarballs. - - -## Quick Install - -* `pip install versioneer` to somewhere in your $PATH -* add a `[versioneer]` section to your setup.cfg (see [Install](INSTALL.md)) -* run `versioneer install` in your source tree, commit the results -* Verify version information with `python setup.py version` - -## Version Identifiers - -Source trees come from a variety of places: - -* a version-control system checkout (mostly used by developers) -* a nightly tarball, produced by build automation -* a snapshot tarball, produced by a web-based VCS browser, like github's - "tarball from tag" feature -* a release tarball, produced by "setup.py sdist", distributed through PyPI - -Within each source tree, the version identifier (either a string or a number, -this tool is format-agnostic) can come from a variety of places: - -* ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows - about recent "tags" and an absolute revision-id -* the name of the directory into which the tarball was unpacked -* an expanded VCS keyword ($Id$, etc) -* a `_version.py` created by some earlier build step - -For released software, the version identifier is closely related to a VCS -tag. Some projects use tag names that include more than just the version -string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool -needs to strip the tag prefix to extract the version identifier. For -unreleased software (between tags), the version identifier should provide -enough information to help developers recreate the same tree, while also -giving them an idea of roughly how old the tree is (after version 1.2, before -version 1.3). Many VCS systems can report a description that captures this, -for example `git describe --tags --dirty --always` reports things like -"0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the -0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has -uncommitted changes). - -The version identifier is used for multiple purposes: - -* to allow the module to self-identify its version: `myproject.__version__` -* to choose a name and prefix for a 'setup.py sdist' tarball - -## Theory of Operation - -Versioneer works by adding a special `_version.py` file into your source -tree, where your `__init__.py` can import it. This `_version.py` knows how to -dynamically ask the VCS tool for version information at import time. - -`_version.py` also contains `$Revision$` markers, and the installation -process marks `_version.py` to have this marker rewritten with a tag name -during the `git archive` command. As a result, generated tarballs will -contain enough information to get the proper version. - -To allow `setup.py` to compute a version too, a `versioneer.py` is added to -the top level of your source tree, next to `setup.py` and the `setup.cfg` -that configures it. This overrides several distutils/setuptools commands to -compute the version when invoked, and changes `setup.py build` and `setup.py -sdist` to replace `_version.py` with a small static file that contains just -the generated version data. - -## Installation - -See [INSTALL.md](./INSTALL.md) for detailed installation instructions. - -## Version-String Flavors - -Code which uses Versioneer can learn about its version string at runtime by -importing `_version` from your main `__init__.py` file and running the -`get_versions()` function. From the "outside" (e.g. in `setup.py`), you can -import the top-level `versioneer.py` and run `get_versions()`. - -Both functions return a dictionary with different flavors of version -information: - -* `['version']`: A condensed version string, rendered using the selected - style. This is the most commonly used value for the project's version - string. The default "pep440" style yields strings like `0.11`, - `0.11+2.g1076c97`, or `0.11+2.g1076c97.dirty`. See the "Styles" section - below for alternative styles. - -* `['full-revisionid']`: detailed revision identifier. For Git, this is the - full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac". - -* `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the - commit date in ISO 8601 format. This will be None if the date is not - available. - -* `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that - this is only accurate if run in a VCS checkout, otherwise it is likely to - be False or None - -* `['error']`: if the version string could not be computed, this will be set - to a string describing the problem, otherwise it will be None. It may be - useful to throw an exception in setup.py if this is set, to avoid e.g. - creating tarballs with a version string of "unknown". - -Some variants are more useful than others. Including `full-revisionid` in a -bug report should allow developers to reconstruct the exact code being tested -(or indicate the presence of local changes that should be shared with the -developers). `version` is suitable for display in an "about" box or a CLI -`--version` output: it can be easily compared against release notes and lists -of bugs fixed in various releases. - -The installer adds the following text to your `__init__.py` to place a basic -version in `YOURPROJECT.__version__`: - - from ._version import get_versions - __version__ = get_versions()['version'] - del get_versions - -## Styles - -The setup.cfg `style=` configuration controls how the VCS information is -rendered into a version string. - -The default style, "pep440", produces a PEP440-compliant string, equal to the -un-prefixed tag name for actual releases, and containing an additional "local -version" section with more detail for in-between builds. For Git, this is -TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe --tags ---dirty --always`. For example "0.11+2.g1076c97.dirty" indicates that the -tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and -that this commit is two revisions ("+2") beyond the "0.11" tag. For released -software (exactly equal to a known tag), the identifier will only contain the -stripped tag, e.g. "0.11". - -Other styles are available. See [details.md](details.md) in the Versioneer -source tree for descriptions. - -## Debugging - -Versioneer tries to avoid fatal errors: if something goes wrong, it will tend -to return a version of "0+unknown". To investigate the problem, run `setup.py -version`, which will run the version-lookup code in a verbose mode, and will -display the full contents of `get_versions()` (including the `error` string, -which may help identify what went wrong). - -## Known Limitations - -Some situations are known to cause problems for Versioneer. This details the -most significant ones. More can be found on Github -[issues page](https://github.com/python-versioneer/python-versioneer/issues). - -### Subprojects - -Versioneer has limited support for source trees in which `setup.py` is not in -the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are -two common reasons why `setup.py` might not be in the root: - -* Source trees which contain multiple subprojects, such as - [Buildbot](https://github.com/buildbot/buildbot), which contains both - "master" and "slave" subprojects, each with their own `setup.py`, - `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI - distributions (and upload multiple independently-installable tarballs). -* Source trees whose main purpose is to contain a C library, but which also - provide bindings to Python (and perhaps other languages) in subdirectories. - -Versioneer will look for `.git` in parent directories, and most operations -should get the right version string. However `pip` and `setuptools` have bugs -and implementation details which frequently cause `pip install .` from a -subproject directory to fail to find a correct version string (so it usually -defaults to `0+unknown`). - -`pip install --editable .` should work correctly. `setup.py install` might -work too. - -Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in -some later version. - -[Bug #38](https://github.com/python-versioneer/python-versioneer/issues/38) is tracking -this issue. The discussion in -[PR #61](https://github.com/python-versioneer/python-versioneer/pull/61) describes the -issue from the Versioneer side in more detail. -[pip PR#3176](https://github.com/pypa/pip/pull/3176) and -[pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve -pip to let Versioneer work correctly. - -Versioneer-0.16 and earlier only looked for a `.git` directory next to the -`setup.cfg`, so subprojects were completely unsupported with those releases. - -### Editable installs with setuptools <= 18.5 - -`setup.py develop` and `pip install --editable .` allow you to install a -project into a virtualenv once, then continue editing the source code (and -test) without re-installing after every change. - -"Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a -convenient way to specify executable scripts that should be installed along -with the python package. - -These both work as expected when using modern setuptools. When using -setuptools-18.5 or earlier, however, certain operations will cause -`pkg_resources.DistributionNotFound` errors when running the entrypoint -script, which must be resolved by re-installing the package. This happens -when the install happens with one version, then the egg_info data is -regenerated while a different version is checked out. Many setup.py commands -cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into -a different virtualenv), so this can be surprising. - -[Bug #83](https://github.com/python-versioneer/python-versioneer/issues/83) describes -this one, but upgrading to a newer version of setuptools should probably -resolve it. - - -## Updating Versioneer - -To upgrade your project to a new release of Versioneer, do the following: - -* install the new Versioneer (`pip install -U versioneer` or equivalent) -* edit `setup.cfg`, if necessary, to include any new configuration settings - indicated by the release notes. See [UPGRADING](./UPGRADING.md) for details. -* re-run `versioneer install` in your source tree, to replace - `SRC/_version.py` -* commit any changed files - -## Future Directions - -This tool is designed to make it easily extended to other version-control -systems: all VCS-specific components are in separate directories like -src/git/ . The top-level `versioneer.py` script is assembled from these -components by running make-versioneer.py . In the future, make-versioneer.py -will take a VCS name as an argument, and will construct a version of -`versioneer.py` that is specific to the given VCS. It might also take the -configuration arguments that are currently provided manually during -installation by editing setup.py . Alternatively, it might go the other -direction and include code from all supported VCS systems, reducing the -number of intermediate scripts. - -## Similar projects - -* [setuptools_scm](https://github.com/pypa/setuptools_scm/) - a non-vendored build-time - dependency -* [minver](https://github.com/jbweston/miniver) - a lightweight reimplementation of - versioneer -* [versioningit](https://github.com/jwodder/versioningit) - a PEP 518-based setuptools - plugin - -## License - -To make Versioneer easier to embed, all its code is dedicated to the public -domain. The `_version.py` that it creates is also in the public domain. -Specifically, both are released under the Creative Commons "Public Domain -Dedication" license (CC0-1.0), as described in -https://creativecommons.org/publicdomain/zero/1.0/ . - -[pypi-image]: https://img.shields.io/pypi/v/versioneer.svg -[pypi-url]: https://pypi.python.org/pypi/versioneer/ -[travis-image]: -https://img.shields.io/travis/com/python-versioneer/python-versioneer.svg -[travis-url]: https://travis-ci.com/github/python-versioneer/python-versioneer - -""" -# pylint:disable=invalid-name,import-outside-toplevel,missing-function-docstring -# pylint:disable=missing-class-docstring,too-many-branches,too-many-statements -# pylint:disable=raise-missing-from,too-many-lines,too-many-locals,import-error -# pylint:disable=too-few-public-methods,redefined-outer-name,consider-using-with -# pylint:disable=attribute-defined-outside-init,too-many-arguments - -import configparser -import errno -import json -import os -import re -import subprocess -import sys -from typing import Callable, Dict -import functools - - -class VersioneerConfig: - """Container for Versioneer configuration parameters.""" - - -def get_root(): - """Get the project root directory. - - We require that all commands are run from the project root, i.e. the - directory that contains setup.py, setup.cfg, and versioneer.py . - """ - root = os.path.realpath(os.path.abspath(os.getcwd())) - setup_py = os.path.join(root, "setup.py") - versioneer_py = os.path.join(root, "versioneer.py") - if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): - # allow 'python path/to/setup.py COMMAND' - root = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0]))) - setup_py = os.path.join(root, "setup.py") - versioneer_py = os.path.join(root, "versioneer.py") - if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): - err = ( - "Versioneer was unable to run the project root directory. " - "Versioneer requires setup.py to be executed from " - "its immediate directory (like 'python setup.py COMMAND'), " - "or in a way that lets it use sys.argv[0] to find the root " - "(like 'python path/to/setup.py COMMAND')." - ) - raise VersioneerBadRootError(err) - try: - # Certain runtime workflows (setup.py install/develop in a setuptools - # tree) execute all dependencies in a single python process, so - # "versioneer" may be imported multiple times, and python's shared - # module-import table will cache the first one. So we can't use - # os.path.dirname(__file__), as that will find whichever - # versioneer.py was first imported, even in later projects. - my_path = os.path.realpath(os.path.abspath(__file__)) - me_dir = os.path.normcase(os.path.splitext(my_path)[0]) - vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0]) - if me_dir != vsr_dir: - print( - "Warning: build in %s is using versioneer.py from %s" - % (os.path.dirname(my_path), versioneer_py) - ) - except NameError: - pass - return root - - -def get_config_from_root(root): - """Read the project setup.cfg file to determine Versioneer config.""" - # This might raise OSError (if setup.cfg is missing), or - # configparser.NoSectionError (if it lacks a [versioneer] section), or - # configparser.NoOptionError (if it lacks "VCS="). See the docstring at - # the top of versioneer.py for instructions on writing your setup.cfg . - setup_cfg = os.path.join(root, "setup.cfg") - parser = configparser.ConfigParser() - with open(setup_cfg, "r") as cfg_file: - parser.read_file(cfg_file) - VCS = parser.get("versioneer", "VCS") # mandatory - - # Dict-like interface for non-mandatory entries - section = parser["versioneer"] - - cfg = VersioneerConfig() - cfg.VCS = VCS - cfg.style = section.get("style", "") - cfg.versionfile_source = section.get("versionfile_source") - cfg.versionfile_build = section.get("versionfile_build") - cfg.tag_prefix = section.get("tag_prefix") - if cfg.tag_prefix in ("''", '""', None): - cfg.tag_prefix = "" - cfg.parentdir_prefix = section.get("parentdir_prefix") - cfg.verbose = section.get("verbose") - return cfg - - -class NotThisMethod(Exception): - """Exception raised if a method is not valid for the current scenario.""" - - -# these dictionaries contain VCS-specific tools -LONG_VERSION_PY: Dict[str, str] = {} -HANDLERS: Dict[str, Dict[str, Callable]] = {} - - -def register_vcs_handler(vcs, method): # decorator - """Create decorator to mark a method as the handler of a VCS.""" - - def decorate(f): - """Store f in HANDLERS[vcs][method].""" - HANDLERS.setdefault(vcs, {})[method] = f - return f - - return decorate - - -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): - """Call the given command(s).""" - assert isinstance(commands, list) - process = None - - popen_kwargs = {} - if sys.platform == "win32": - # This hides the console window if pythonw.exe is used - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - popen_kwargs["startupinfo"] = startupinfo - - for command in commands: - try: - dispcmd = str([command] + args) - # remember shell=False, so use git.cmd on windows, not just git - process = subprocess.Popen( - [command] + args, - cwd=cwd, - env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr else None), - **popen_kwargs, - ) - break - except OSError: - e = sys.exc_info()[1] - if e.errno == errno.ENOENT: - continue - if verbose: - print("unable to run %s" % dispcmd) - print(e) - return None, None - else: - if verbose: - print("unable to find command, tried %s" % (commands,)) - return None, None - stdout = process.communicate()[0].strip().decode() - if process.returncode != 0: - if verbose: - print("unable to run %s (error)" % dispcmd) - print("stdout was %s" % stdout) - return None, process.returncode - return stdout, process.returncode - - -LONG_VERSION_PY[ - "git" -] = r''' -# This file helps to compute a version number in source trees obtained from -# git-archive tarball (such as those provided by githubs download-from-tag -# feature). Distribution tarballs (built by setup.py sdist) and build -# directories (produced by setup.py build) will contain a much shorter file -# that just contains the computed version number. - -# This file is released into the public domain. Generated by -# versioneer-0.23 (https://github.com/python-versioneer/python-versioneer) - -"""Git implementation of _version.py.""" - -import errno -import os -import re -import subprocess -import sys -from typing import Callable, Dict -import functools - - -def get_keywords(): - """Get the keywords needed to look up the version information.""" - # these strings will be replaced by git during git-archive. - # setup.py/versioneer.py will grep for the variable names, so they must - # each be defined on a line of their own. _version.py will just call - # get_keywords(). - git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s" - git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s" - git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s" - keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} - return keywords - - -class VersioneerConfig: - """Container for Versioneer configuration parameters.""" - - -def get_config(): - """Create, populate and return the VersioneerConfig() object.""" - # these strings are filled in when 'setup.py versioneer' creates - # _version.py - cfg = VersioneerConfig() - cfg.VCS = "git" - cfg.style = "%(STYLE)s" - cfg.tag_prefix = "%(TAG_PREFIX)s" - cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s" - cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s" - cfg.verbose = False - return cfg - - -class NotThisMethod(Exception): - """Exception raised if a method is not valid for the current scenario.""" - - -LONG_VERSION_PY: Dict[str, str] = {} -HANDLERS: Dict[str, Dict[str, Callable]] = {} - - -def register_vcs_handler(vcs, method): # decorator - """Create decorator to mark a method as the handler of a VCS.""" - def decorate(f): - """Store f in HANDLERS[vcs][method].""" - if vcs not in HANDLERS: - HANDLERS[vcs] = {} - HANDLERS[vcs][method] = f - return f - return decorate - - -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, - env=None): - """Call the given command(s).""" - assert isinstance(commands, list) - process = None - - popen_kwargs = {} - if sys.platform == "win32": - # This hides the console window if pythonw.exe is used - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - popen_kwargs["startupinfo"] = startupinfo - - for command in commands: - try: - dispcmd = str([command] + args) - # remember shell=False, so use git.cmd on windows, not just git - process = subprocess.Popen([command] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None), **popen_kwargs) - break - except OSError: - e = sys.exc_info()[1] - if e.errno == errno.ENOENT: - continue - if verbose: - print("unable to run %%s" %% dispcmd) - print(e) - return None, None - else: - if verbose: - print("unable to find command, tried %%s" %% (commands,)) - return None, None - stdout = process.communicate()[0].strip().decode() - if process.returncode != 0: - if verbose: - print("unable to run %%s (error)" %% dispcmd) - print("stdout was %%s" %% stdout) - return None, process.returncode - return stdout, process.returncode - - -def versions_from_parentdir(parentdir_prefix, root, verbose): - """Try to determine the version from the parent directory name. - - Source tarballs conventionally unpack into a directory that includes both - the project name and a version string. We will also support searching up - two directory levels for an appropriately named parent directory - """ - rootdirs = [] - - for _ in range(3): - dirname = os.path.basename(root) - if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} - rootdirs.append(root) - root = os.path.dirname(root) # up a level - - if verbose: - print("Tried directories %%s but none started with prefix %%s" %% - (str(rootdirs), parentdir_prefix)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs): - """Extract version information from the given file.""" - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords = {} - try: - with open(versionfile_abs, "r") as fobj: - for line in fobj: - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - if line.strip().startswith("git_date ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["date"] = mo.group(1) - except OSError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords(keywords, tag_prefix, verbose): - """Get version information from git keywords.""" - if "refnames" not in keywords: - raise NotThisMethod("Short version file found") - date = keywords.get("date") - if date is not None: - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - - # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant - # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601 - # -like" string, which we must then edit to make compliant), because - # it's been around since git-1.5.3, and it's too difficult to - # discover which version we're using, or to work around using an - # older one. - date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = {r.strip() for r in refnames.strip("()").split(",")} - # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of - # just "foo-1.0". If we see a "tag: " prefix, prefer those. - TAG = "tag: " - tags = {r[len(TAG):] for r in refs if r.startswith(TAG)} - if not tags: - # Either we're using git < 1.8.3, or there really are no tags. We use - # a heuristic: assume all version tags have a digit. The old git %%d - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # between branches and tags. By ignoring refnames without digits, we - # filter out many common branch names like "release" and - # "stabilization", as well as "HEAD" and "master". - tags = {r for r in refs if re.search(r'\d', r)} - if verbose: - print("discarding '%%s', no digits" %% ",".join(refs - tags)) - if verbose: - print("likely tags: %%s" %% ",".join(sorted(tags))) - for ref in sorted(tags): - # sorting will prefer e.g. "2.0" over "2.0rc1" - if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] - # Filter out refs that exactly match prefix or that don't start - # with a number once the prefix is stripped (mostly a concern - # when prefix is '') - if not re.match(r'\d', r): - continue - if verbose: - print("picking %%s" %% r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} - # no suitable tags, so version is "0+unknown", but full hex is still there - if verbose: - print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): - """Get version from 'git describe' in the root of the source tree. - - This only gets called if the git-archive 'subst' keywords were *not* - expanded, and _version.py hasn't already been rewritten with a short - version string, meaning we're inside a checked out source tree. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - - # GIT_DIR can interfere with correct operation of Versioneer. - # It may be intended to be passed to the Versioneer-versioned project, - # but that should not change where we get our version from. - env = os.environ.copy() - env.pop("GIT_DIR", None) - runner = functools.partial(runner, env=env) - - _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, - hide_stderr=True) - if rc != 0: - if verbose: - print("Directory %%s not under git control" %% root) - raise NotThisMethod("'git rev-parse --git-dir' returned error") - - # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] - # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = runner(GITS, [ - "describe", "--tags", "--dirty", "--always", "--long", - "--match", f"{tag_prefix}[[:digit:]]*" - ], cwd=root) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], - cwd=root) - # --abbrev-ref was added in git-1.6.3 - if rc != 0 or branch_name is None: - raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") - branch_name = branch_name.strip() - - if branch_name == "HEAD": - # If we aren't exactly on a branch, pick a branch which represents - # the current commit. If all else fails, we are on a branchless - # commit. - branches, rc = runner(GITS, ["branch", "--contains"], cwd=root) - # --contains was added in git-1.5.4 - if rc != 0 or branches is None: - raise NotThisMethod("'git branch --contains' returned error") - branches = branches.split("\n") - - # Remove the first line if we're running detached - if "(" in branches[0]: - branches.pop(0) - - # Strip off the leading "* " from the list of branches. - branches = [branch[2:] for branch in branches] - if "master" in branches: - branch_name = "master" - elif not branches: - branch_name = None - else: - # Pick the first branch that is returned. Good or bad. - branch_name = branches[0] - - pieces["branch"] = branch_name - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] - - # now we have TAG-NUM-gHEX or HEX - - if "-" in git_describe: - # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) - if not mo: - # unparsable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%%s'" - %% describe_out) - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - if verbose: - fmt = "tag '%%s' doesn't start with prefix '%%s'" - print(fmt %% (full_tag, tag_prefix)) - pieces["error"] = ("tag '%%s' doesn't start with prefix '%%s'" - %% (full_tag, tag_prefix)) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root) - pieces["distance"] = len(out.split()) # total number of commits - - # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = runner(GITS, ["show", "-s", "--format=%%ci", "HEAD"], cwd=root)[0].strip() - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - - return pieces - - -def plus_or_dot(pieces): - """Return a + if we don't already have one, else return a .""" - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces): - """Build up version string, with post-release "local version identifier". - - Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - Exceptions: - 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"], - pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_branch(pieces): - """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] . - - The ".dev0" means not master branch. Note that .dev0 sorts backwards - (a feature branch will appear "older" than the master branch). - - Exceptions: - 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0" - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += "+untagged.%%d.g%%s" %% (pieces["distance"], - pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def pep440_split_post(ver): - """Split pep440 version string at the post-release segment. - - Returns the release segments before the post-release and the - post-release version number (or -1 if no post-release segment is present). - """ - vc = str.split(ver, ".post") - return vc[0], int(vc[1] or 0) if len(vc) == 2 else None - - -def render_pep440_pre(pieces): - """TAG[.postN.devDISTANCE] -- No -dirty. - - Exceptions: - 1: no tags. 0.post0.devDISTANCE - """ - if pieces["closest-tag"]: - if pieces["distance"]: - # update the post release segment - tag_version, post_version = pep440_split_post(pieces["closest-tag"]) - rendered = tag_version - if post_version is not None: - rendered += ".post%%d.dev%%d" %% (post_version + 1, pieces["distance"]) - else: - rendered += ".post0.dev%%d" %% (pieces["distance"]) - else: - # no commits, use the tag as the version - rendered = pieces["closest-tag"] - else: - # exception #1 - rendered = "0.post0.dev%%d" %% pieces["distance"] - return rendered - - -def render_pep440_post(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX] . - - The ".dev0" means dirty. Note that .dev0 sorts backwards - (a dirty tree will appear "older" than the corresponding clean one), - but you shouldn't be releasing software with -dirty anyways. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%%s" %% pieces["short"] - else: - # exception #1 - rendered = "0.post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += "+g%%s" %% pieces["short"] - return rendered - - -def render_pep440_post_branch(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] . - - The ".dev0" means not master branch. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%%d" %% pieces["distance"] - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%%s" %% pieces["short"] - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0.post%%d" %% pieces["distance"] - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += "+g%%s" %% pieces["short"] - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_old(pieces): - """TAG[.postDISTANCE[.dev0]] . - - The ".dev0" means dirty. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = "0.post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces): - """TAG[-DISTANCE-gHEX][-dirty]. - - Like 'git describe --tags --dirty --always'. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces): - """TAG-DISTANCE-gHEX[-dirty]. - - Like 'git describe --tags --dirty --always -long'. - The distance/hash is unconditional. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render(pieces, style): - """Render the given version pieces into the requested style.""" - if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-branch": - rendered = render_pep440_branch(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-post-branch": - rendered = render_pep440_post_branch(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError("unknown style '%%s'" %% style) - - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} - - -def get_versions(): - """Get version information or return default if unable to do so.""" - # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have - # __file__, we can work backwards from there to the root. Some - # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which - # case we can only use expanded keywords. - - cfg = get_config() - verbose = cfg.verbose - - try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, - verbose) - except NotThisMethod: - pass - - try: - root = os.path.realpath(__file__) - # versionfile_source is the relative path from the top of the source - # tree (where the .git directory might live) to this file. Invert - # this to find the root from __file__. - for _ in cfg.versionfile_source.split('/'): - root = os.path.dirname(root) - except NameError: - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None} - - try: - pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) - return render(pieces, cfg.style) - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - except NotThisMethod: - pass - - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", "date": None} -''' - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs): - """Extract version information from the given file.""" - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords = {} - try: - with open(versionfile_abs, "r") as fobj: - for line in fobj: - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - if line.strip().startswith("git_date ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["date"] = mo.group(1) - except OSError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords(keywords, tag_prefix, verbose): - """Get version information from git keywords.""" - if "refnames" not in keywords: - raise NotThisMethod("Short version file found") - date = keywords.get("date") - if date is not None: - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - - # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant - # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 - # -like" string, which we must then edit to make compliant), because - # it's been around since git-1.5.3, and it's too difficult to - # discover which version we're using, or to work around using an - # older one. - date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = {r.strip() for r in refnames.strip("()").split(",")} - # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of - # just "foo-1.0". If we see a "tag: " prefix, prefer those. - TAG = "tag: " - tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)} - if not tags: - # Either we're using git < 1.8.3, or there really are no tags. We use - # a heuristic: assume all version tags have a digit. The old git %d - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # between branches and tags. By ignoring refnames without digits, we - # filter out many common branch names like "release" and - # "stabilization", as well as "HEAD" and "master". - tags = {r for r in refs if re.search(r"\d", r)} - if verbose: - print("discarding '%s', no digits" % ",".join(refs - tags)) - if verbose: - print("likely tags: %s" % ",".join(sorted(tags))) - for ref in sorted(tags): - # sorting will prefer e.g. "2.0" over "2.0rc1" - if ref.startswith(tag_prefix): - r = ref[len(tag_prefix) :] - # Filter out refs that exactly match prefix or that don't start - # with a number once the prefix is stripped (mostly a concern - # when prefix is '') - if not re.match(r"\d", r): - continue - if verbose: - print("picking %s" % r) - return { - "version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, - "error": None, - "date": date, - } - # no suitable tags, so version is "0+unknown", but full hex is still there - if verbose: - print("no suitable tags, using unknown + full revision id") - return { - "version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, - "error": "no suitable tags", - "date": None, - } - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): - """Get version from 'git describe' in the root of the source tree. - - This only gets called if the git-archive 'subst' keywords were *not* - expanded, and _version.py hasn't already been rewritten with a short - version string, meaning we're inside a checked out source tree. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - - # GIT_DIR can interfere with correct operation of Versioneer. - # It may be intended to be passed to the Versioneer-versioned project, - # but that should not change where we get our version from. - env = os.environ.copy() - env.pop("GIT_DIR", None) - runner = functools.partial(runner, env=env) - - _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) - if rc != 0: - if verbose: - print("Directory %s not under git control" % root) - raise NotThisMethod("'git rev-parse --git-dir' returned error") - - # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] - # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = runner( - GITS, - [ - "describe", - "--tags", - "--dirty", - "--always", - "--long", - "--match", - f"{tag_prefix}[[:digit:]]*", - ], - cwd=root, - ) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root) - # --abbrev-ref was added in git-1.6.3 - if rc != 0 or branch_name is None: - raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") - branch_name = branch_name.strip() - - if branch_name == "HEAD": - # If we aren't exactly on a branch, pick a branch which represents - # the current commit. If all else fails, we are on a branchless - # commit. - branches, rc = runner(GITS, ["branch", "--contains"], cwd=root) - # --contains was added in git-1.5.4 - if rc != 0 or branches is None: - raise NotThisMethod("'git branch --contains' returned error") - branches = branches.split("\n") - - # Remove the first line if we're running detached - if "(" in branches[0]: - branches.pop(0) - - # Strip off the leading "* " from the list of branches. - branches = [branch[2:] for branch in branches] - if "master" in branches: - branch_name = "master" - elif not branches: - branch_name = None - else: - # Pick the first branch that is returned. Good or bad. - branch_name = branches[0] - - pieces["branch"] = branch_name - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if dirty: - git_describe = git_describe[: git_describe.rindex("-dirty")] - - # now we have TAG-NUM-gHEX or HEX - - if "-" in git_describe: - # TAG-NUM-gHEX - mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) - if not mo: - # unparsable. Maybe git-describe is misbehaving? - pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - if verbose: - fmt = "tag '%s' doesn't start with prefix '%s'" - print(fmt % (full_tag, tag_prefix)) - pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( - full_tag, - tag_prefix, - ) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix) :] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root) - pieces["distance"] = len(out.split()) # total number of commits - - # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - - return pieces - - -def do_vcs_install(versionfile_source, ipy): - """Git-specific installation logic for Versioneer. - - For Git, this means creating/changing .gitattributes to mark _version.py - for export-subst keyword substitution. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - files = [versionfile_source] - if ipy: - files.append(ipy) - try: - my_path = __file__ - if my_path.endswith(".pyc") or my_path.endswith(".pyo"): - my_path = os.path.splitext(my_path)[0] + ".py" - versioneer_file = os.path.relpath(my_path) - except NameError: - versioneer_file = "versioneer.py" - files.append(versioneer_file) - present = False - try: - with open(".gitattributes", "r") as fobj: - for line in fobj: - if line.strip().startswith(versionfile_source): - if "export-subst" in line.strip().split()[1:]: - present = True - break - except OSError: - pass - if not present: - with open(".gitattributes", "a+") as fobj: - fobj.write(f"{versionfile_source} export-subst\n") - files.append(".gitattributes") - run_command(GITS, ["add", "--"] + files) - - -def versions_from_parentdir(parentdir_prefix, root, verbose): - """Try to determine the version from the parent directory name. - - Source tarballs conventionally unpack into a directory that includes both - the project name and a version string. We will also support searching up - two directory levels for an appropriately named parent directory - """ - rootdirs = [] - - for _ in range(3): - dirname = os.path.basename(root) - if dirname.startswith(parentdir_prefix): - return { - "version": dirname[len(parentdir_prefix) :], - "full-revisionid": None, - "dirty": False, - "error": None, - "date": None, - } - rootdirs.append(root) - root = os.path.dirname(root) # up a level - - if verbose: - print( - "Tried directories %s but none started with prefix %s" - % (str(rootdirs), parentdir_prefix) - ) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - - -SHORT_VERSION_PY = """ -# This file was generated by 'versioneer.py' (0.23) from -# revision-control system data, or from the parent directory name of an -# unpacked source archive. Distribution tarballs contain a pre-generated copy -# of this file. - -import json - -version_json = ''' -%s -''' # END VERSION_JSON - - -def get_versions(): - return json.loads(version_json) -""" - - -def versions_from_file(filename): - """Try to determine the version from _version.py if present.""" - try: - with open(filename) as f: - contents = f.read() - except OSError: - raise NotThisMethod("unable to read _version.py") - mo = re.search( - r"version_json = '''\n(.*)''' # END VERSION_JSON", contents, re.M | re.S - ) - if not mo: - mo = re.search( - r"version_json = '''\r\n(.*)''' # END VERSION_JSON", contents, re.M | re.S - ) - if not mo: - raise NotThisMethod("no version_json in _version.py") - return json.loads(mo.group(1)) - - -def write_to_version_file(filename, versions): - """Write the given version number to the given _version.py file.""" - os.unlink(filename) - contents = json.dumps(versions, sort_keys=True, indent=1, separators=(",", ": ")) - with open(filename, "w") as f: - f.write(SHORT_VERSION_PY % contents) - - print("set %s to '%s'" % (filename, versions["version"])) - - -def plus_or_dot(pieces): - """Return a + if we don't already have one, else return a .""" - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces): - """Build up version string, with post-release "local version identifier". - - Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - Exceptions: - 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_branch(pieces): - """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] . - - The ".dev0" means not master branch. Note that .dev0 sorts backwards - (a feature branch will appear "older" than the master branch). - - Exceptions: - 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0" - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += "+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def pep440_split_post(ver): - """Split pep440 version string at the post-release segment. - - Returns the release segments before the post-release and the - post-release version number (or -1 if no post-release segment is present). - """ - vc = str.split(ver, ".post") - return vc[0], int(vc[1] or 0) if len(vc) == 2 else None - - -def render_pep440_pre(pieces): - """TAG[.postN.devDISTANCE] -- No -dirty. - - Exceptions: - 1: no tags. 0.post0.devDISTANCE - """ - if pieces["closest-tag"]: - if pieces["distance"]: - # update the post release segment - tag_version, post_version = pep440_split_post(pieces["closest-tag"]) - rendered = tag_version - if post_version is not None: - rendered += ".post%d.dev%d" % (post_version + 1, pieces["distance"]) - else: - rendered += ".post0.dev%d" % (pieces["distance"]) - else: - # no commits, use the tag as the version - rendered = pieces["closest-tag"] - else: - # exception #1 - rendered = "0.post0.dev%d" % pieces["distance"] - return rendered - - -def render_pep440_post(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX] . - - The ".dev0" means dirty. Note that .dev0 sorts backwards - (a dirty tree will appear "older" than the corresponding clean one), - but you shouldn't be releasing software with -dirty anyways. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%s" % pieces["short"] - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += "+g%s" % pieces["short"] - return rendered - - -def render_pep440_post_branch(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] . - - The ".dev0" means not master branch. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%s" % pieces["short"] - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += "+g%s" % pieces["short"] - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_old(pieces): - """TAG[.postDISTANCE[.dev0]] . - - The ".dev0" means dirty. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces): - """TAG[-DISTANCE-gHEX][-dirty]. - - Like 'git describe --tags --dirty --always'. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces): - """TAG-DISTANCE-gHEX[-dirty]. - - Like 'git describe --tags --dirty --always -long'. - The distance/hash is unconditional. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render(pieces, style): - """Render the given version pieces into the requested style.""" - if pieces["error"]: - return { - "version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None, - } - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-branch": - rendered = render_pep440_branch(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-post-branch": - rendered = render_pep440_post_branch(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError("unknown style '%s'" % style) - - return { - "version": rendered, - "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], - "error": None, - "date": pieces.get("date"), - } - - -class VersioneerBadRootError(Exception): - """The project root directory is unknown or missing key files.""" - - -def get_versions(verbose=False): - """Get the project version from whatever source is available. - - Returns dict with two keys: 'version' and 'full'. - """ - if "versioneer" in sys.modules: - # see the discussion in cmdclass.py:get_cmdclass() - del sys.modules["versioneer"] - - root = get_root() - cfg = get_config_from_root(root) - - assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg" - handlers = HANDLERS.get(cfg.VCS) - assert handlers, "unrecognized VCS '%s'" % cfg.VCS - verbose = verbose or cfg.verbose - assert ( - cfg.versionfile_source is not None - ), "please set versioneer.versionfile_source" - assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix" - - versionfile_abs = os.path.join(root, cfg.versionfile_source) - - # extract version from first of: _version.py, VCS command (e.g. 'git - # describe'), parentdir. This is meant to work for developers using a - # source checkout, for users of a tarball created by 'setup.py sdist', - # and for users of a tarball/zipball created by 'git archive' or github's - # download-from-tag feature or the equivalent in other VCSes. - - get_keywords_f = handlers.get("get_keywords") - from_keywords_f = handlers.get("keywords") - if get_keywords_f and from_keywords_f: - try: - keywords = get_keywords_f(versionfile_abs) - ver = from_keywords_f(keywords, cfg.tag_prefix, verbose) - if verbose: - print("got version from expanded keyword %s" % ver) - return ver - except NotThisMethod: - pass - - try: - ver = versions_from_file(versionfile_abs) - if verbose: - print("got version from file %s %s" % (versionfile_abs, ver)) - return ver - except NotThisMethod: - pass - - from_vcs_f = handlers.get("pieces_from_vcs") - if from_vcs_f: - try: - pieces = from_vcs_f(cfg.tag_prefix, root, verbose) - ver = render(pieces, cfg.style) - if verbose: - print("got version from VCS %s" % ver) - return ver - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - if verbose: - print("got version from parentdir %s" % ver) - return ver - except NotThisMethod: - pass - - if verbose: - print("unable to compute version") - - return { - "version": "0+unknown", - "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", - "date": None, - } - - -def get_version(): - """Get the short version string for this project.""" - return get_versions()["version"] - - -def get_cmdclass(cmdclass=None): - """Get the custom setuptools subclasses used by Versioneer. - - If the package uses a different cmdclass (e.g. one from numpy), it - should be provide as an argument. - """ - if "versioneer" in sys.modules: - del sys.modules["versioneer"] - # this fixes the "python setup.py develop" case (also 'install' and - # 'easy_install .'), in which subdependencies of the main project are - # built (using setup.py bdist_egg) in the same python process. Assume - # a main project A and a dependency B, which use different versions - # of Versioneer. A's setup.py imports A's Versioneer, leaving it in - # sys.modules by the time B's setup.py is executed, causing B to run - # with the wrong versioneer. Setuptools wraps the sub-dep builds in a - # sandbox that restores sys.modules to it's pre-build state, so the - # parent is protected against the child's "import versioneer". By - # removing ourselves from sys.modules here, before the child build - # happens, we protect the child from the parent's versioneer too. - # Also see https://github.com/python-versioneer/python-versioneer/issues/52 - - cmds = {} if cmdclass is None else cmdclass.copy() - - # we add "version" to setuptools - from setuptools import Command - - class cmd_version(Command): - description = "report generated version string" - user_options = [] - boolean_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - vers = get_versions(verbose=True) - print("Version: %s" % vers["version"]) - print(" full-revisionid: %s" % vers.get("full-revisionid")) - print(" dirty: %s" % vers.get("dirty")) - print(" date: %s" % vers.get("date")) - if vers["error"]: - print(" error: %s" % vers["error"]) - - cmds["version"] = cmd_version - - # we override "build_py" in setuptools - # - # most invocation pathways end up running build_py: - # distutils/build -> build_py - # distutils/install -> distutils/build ->.. - # setuptools/bdist_wheel -> distutils/install ->.. - # setuptools/bdist_egg -> distutils/install_lib -> build_py - # setuptools/install -> bdist_egg ->.. - # setuptools/develop -> ? - # pip install: - # copies source tree to a tempdir before running egg_info/etc - # if .git isn't copied too, 'git describe' will fail - # then does setup.py bdist_wheel, or sometimes setup.py install - # setup.py egg_info -> ? - - # pip install -e . and setuptool/editable_wheel will invoke build_py - # but the build_py command is not expected to copy any files. - - # we override different "build_py" commands for both environments - if "build_py" in cmds: - _build_py = cmds["build_py"] - else: - from setuptools.command.build_py import build_py as _build_py - - class cmd_build_py(_build_py): - def run(self): - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - _build_py.run(self) - if getattr(self, "editable_mode", False): - # During editable installs `.py` and data files are - # not copied to build_lib - return - # now locate _version.py in the new build/ directory and replace - # it with an updated value - if cfg.versionfile_build: - target_versionfile = os.path.join(self.build_lib, cfg.versionfile_build) - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - - cmds["build_py"] = cmd_build_py - - if "build_ext" in cmds: - _build_ext = cmds["build_ext"] - else: - from setuptools.command.build_ext import build_ext as _build_ext - - class cmd_build_ext(_build_ext): - def run(self): - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - _build_ext.run(self) - if self.inplace: - # build_ext --inplace will only build extensions in - # build/lib<..> dir with no _version.py to write to. - # As in place builds will already have a _version.py - # in the module dir, we do not need to write one. - return - # now locate _version.py in the new build/ directory and replace - # it with an updated value - target_versionfile = os.path.join(self.build_lib, cfg.versionfile_build) - if not os.path.exists(target_versionfile): - print( - f"Warning: {target_versionfile} does not exist, skipping " - "version update. This can happen if you are running build_ext " - "without first running build_py." - ) - return - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - - cmds["build_ext"] = cmd_build_ext - - if "cx_Freeze" in sys.modules: # cx_freeze enabled? - from cx_Freeze.dist import build_exe as _build_exe - - # nczeczulin reports that py2exe won't like the pep440-style string - # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g. - # setup(console=[{ - # "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION - # "product_version": versioneer.get_version(), - # ... - - class cmd_build_exe(_build_exe): - def run(self): - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - target_versionfile = cfg.versionfile_source - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - - _build_exe.run(self) - os.unlink(target_versionfile) - with open(cfg.versionfile_source, "w") as f: - LONG = LONG_VERSION_PY[cfg.VCS] - f.write( - LONG - % { - "DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - } - ) - - cmds["build_exe"] = cmd_build_exe - del cmds["build_py"] - - if "py2exe" in sys.modules: # py2exe enabled? - from py2exe.distutils_buildexe import py2exe as _py2exe - - class cmd_py2exe(_py2exe): - def run(self): - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - target_versionfile = cfg.versionfile_source - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - - _py2exe.run(self) - os.unlink(target_versionfile) - with open(cfg.versionfile_source, "w") as f: - LONG = LONG_VERSION_PY[cfg.VCS] - f.write( - LONG - % { - "DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - } - ) - - cmds["py2exe"] = cmd_py2exe - - # sdist farms its file list building out to egg_info - if "egg_info" in cmds: - _sdist = cmds["egg_info"] - else: - from setuptools.command.egg_info import egg_info as _egg_info - - class cmd_egg_info(_egg_info): - def find_sources(self): - # egg_info.find_sources builds the manifest list and writes it - # in one shot - super().find_sources() - - # Modify the filelist and normalize it - root = get_root() - cfg = get_config_from_root(root) - self.filelist.append("versioneer.py") - if cfg.versionfile_source: - # There are rare cases where versionfile_source might not be - # included by default, so we must be explicit - self.filelist.append(cfg.versionfile_source) - self.filelist.sort() - self.filelist.remove_duplicates() - - # The write method is hidden in the manifest_maker instance that - # generated the filelist and was thrown away - # We will instead replicate their final normalization (to unicode, - # and POSIX-style paths) - from setuptools import unicode_utils - - normalized = [ - unicode_utils.filesys_decode(f).replace(os.sep, "/") - for f in self.filelist.files - ] - - manifest_filename = os.path.join(self.egg_info, "SOURCES.txt") - with open(manifest_filename, "w") as fobj: - fobj.write("\n".join(normalized)) - - cmds["egg_info"] = cmd_egg_info - - # we override different "sdist" commands for both environments - if "sdist" in cmds: - _sdist = cmds["sdist"] - else: - from setuptools.command.sdist import sdist as _sdist - - class cmd_sdist(_sdist): - def run(self): - versions = get_versions() - self._versioneer_generated_versions = versions - # unless we update this, the command will keep using the old - # version - self.distribution.metadata.version = versions["version"] - return _sdist.run(self) - - def make_release_tree(self, base_dir, files): - root = get_root() - cfg = get_config_from_root(root) - _sdist.make_release_tree(self, base_dir, files) - # now locate _version.py in the new base_dir directory - # (remembering that it may be a hardlink) and replace it with an - # updated value - target_versionfile = os.path.join(base_dir, cfg.versionfile_source) - print("UPDATING %s" % target_versionfile) - write_to_version_file( - target_versionfile, self._versioneer_generated_versions - ) - - cmds["sdist"] = cmd_sdist - - return cmds - - -CONFIG_ERROR = """ -setup.cfg is missing the necessary Versioneer configuration. You need -a section like: - - [versioneer] - VCS = git - style = pep440 - versionfile_source = src/myproject/_version.py - versionfile_build = myproject/_version.py - tag_prefix = - parentdir_prefix = myproject- - -You will also need to edit your setup.py to use the results: - - import versioneer - setup(version=versioneer.get_version(), - cmdclass=versioneer.get_cmdclass(), ...) - -Please read the docstring in ./versioneer.py for configuration instructions, -edit setup.cfg, and re-run the installer or 'python versioneer.py setup'. -""" - -SAMPLE_CONFIG = """ -# See the docstring in versioneer.py for instructions. Note that you must -# re-run 'versioneer.py setup' after changing this section, and commit the -# resulting files. - -[versioneer] -#VCS = git -#style = pep440 -#versionfile_source = -#versionfile_build = -#tag_prefix = -#parentdir_prefix = - -""" - -OLD_SNIPPET = """ -from ._version import get_versions -__version__ = get_versions()['version'] -del get_versions -""" - -INIT_PY_SNIPPET = """ -from . import {0} -__version__ = {0}.get_versions()['version'] -""" - - -def do_setup(): - """Do main VCS-independent setup function for installing Versioneer.""" - root = get_root() - try: - cfg = get_config_from_root(root) - except (OSError, configparser.NoSectionError, configparser.NoOptionError) as e: - if isinstance(e, (OSError, configparser.NoSectionError)): - print("Adding sample versioneer config to setup.cfg", file=sys.stderr) - with open(os.path.join(root, "setup.cfg"), "a") as f: - f.write(SAMPLE_CONFIG) - print(CONFIG_ERROR, file=sys.stderr) - return 1 - - print(" creating %s" % cfg.versionfile_source) - with open(cfg.versionfile_source, "w") as f: - LONG = LONG_VERSION_PY[cfg.VCS] - f.write( - LONG - % { - "DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - } - ) - - ipy = os.path.join(os.path.dirname(cfg.versionfile_source), "__init__.py") - if os.path.exists(ipy): - try: - with open(ipy, "r") as f: - old = f.read() - except OSError: - old = "" - module = os.path.splitext(os.path.basename(cfg.versionfile_source))[0] - snippet = INIT_PY_SNIPPET.format(module) - if OLD_SNIPPET in old: - print(" replacing boilerplate in %s" % ipy) - with open(ipy, "w") as f: - f.write(old.replace(OLD_SNIPPET, snippet)) - elif snippet not in old: - print(" appending to %s" % ipy) - with open(ipy, "a") as f: - f.write(snippet) - else: - print(" %s unmodified" % ipy) - else: - print(" %s doesn't exist, ok" % ipy) - ipy = None - - # Make VCS-specific changes. For git, this means creating/changing - # .gitattributes to mark _version.py for export-subst keyword - # substitution. - do_vcs_install(cfg.versionfile_source, ipy) - return 0 - - -def scan_setup_py(): - """Validate the contents of setup.py against Versioneer's expectations.""" - found = set() - setters = False - errors = 0 - with open("setup.py", "r") as f: - for line in f.readlines(): - if "import versioneer" in line: - found.add("import") - if "versioneer.get_cmdclass()" in line: - found.add("cmdclass") - if "versioneer.get_version()" in line: - found.add("get_version") - if "versioneer.VCS" in line: - setters = True - if "versioneer.versionfile_source" in line: - setters = True - if len(found) != 3: - print("") - print("Your setup.py appears to be missing some important items") - print("(but I might be wrong). Please make sure it has something") - print("roughly like the following:") - print("") - print(" import versioneer") - print(" setup( version=versioneer.get_version(),") - print(" cmdclass=versioneer.get_cmdclass(), ...)") - print("") - errors += 1 - if setters: - print("You should remove lines like 'versioneer.VCS = ' and") - print("'versioneer.versionfile_source = ' . This configuration") - print("now lives in setup.cfg, and should be removed from setup.py") - print("") - errors += 1 - return errors - - -if __name__ == "__main__": - cmd = sys.argv[1] - if cmd == "setup": - errors = do_setup() - errors += scan_setup_py() - if errors: - sys.exit(1) diff --git a/development_environment b/development_environment index 9888abe64e..d1da73e11c 100644 --- a/development_environment +++ b/development_environment @@ -153,25 +153,3 @@ PUBLIC_JWT_SIGNING_KEY=LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0NCk1JSUNJakFOQmdrcWhra # Worked examples WORKED_EXAMPLES_PORT=8888 - -## AutoFlow settings -# Directory containing AutoFlow input files -AUTOFLOW_INPUTS_DIR=./autoflow/examples/inputs -# Directory to store AutoFlow output files -AUTOFLOW_OUTPUTS_DIR=./autoflow/examples/outputs -# Error and debugging log level -AUTOFLOW_LOG_LEVEL=DEBUG -# Path to prefect config file -PREFECT__USER_CONFIG_PATH=./autoflow/config/config.toml -# Path to asciidoc template -PREFECT__ASCIIDOC_TEMPLATE_PATH=./autoflow/config/asciidoc_extended.tpl -# FlowAPI URL -FLOWAPI_URL=http://localhost:9090 -# URI of database for storing workflow run metadata -AUTOFLOW_DB_URI=sqlite:////tmp/test.db -# Password for AutoFlow database -#AUTOFLOW_DB_PASSWORD -# FlowAPI token -#FLOWAPI_TOKEN -# Path to SSL certificate for FlowAPI -#SSL_CERTIFICATE_FILE diff --git a/docker-compose-build.yml b/docker-compose-build.yml index 8c31186a03..e720e833b4 100644 --- a/docker-compose-build.yml +++ b/docker-compose-build.yml @@ -45,9 +45,3 @@ services: build: context: . dockerfile: flowetl.Dockerfile - - autoflow: - image: flowminder/autoflow:${CONTAINER_TAG:-latest} - build: - context: . - dockerfile: autoflow.Dockerfile diff --git a/docker-compose.autoflow.yml b/docker-compose.autoflow.yml deleted file mode 100644 index d6260959a7..0000000000 --- a/docker-compose.autoflow.yml +++ /dev/null @@ -1,25 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# DOCKER COMPOSE FOR FLOWKIT WITH AUTOFLOW -# - -version: '3.5' - -services: - autoflow: - container_name: autoflow - image: flowminder/autoflow:${CONTAINER_TAG:-latest} - restart: always - volumes: - - ${AUTOFLOW_INPUTS_DIR:?Must set AUTOFLOW_INPUTS_DIR env var}:/mounts/inputs:ro - - ${AUTOFLOW_OUTPUTS_DIR:?Must set AUTOFLOW_OUTPUTS_DIR env var}:/mounts/outputs:rw - environment: - AUTOFLOW_LOG_LEVEL: ${AUTOFLOW_LOG_LEVEL:?Must set AUTOFLOW_LOG_LEVEL env var} - AUTOFLOW_DB_URI: ${AUTOFLOW_DB_URI:?Must set AUTOFLOW_DB_URI env var} - AUTOFLOW_DB_PASSWORD: ${AUTOFLOW_DB_PASSWORD} - FLOWAPI_URL: http://flowapi:${FLOWAPI_PORT:?Must set FLOWAPI_PORT env var} - FLOWAPI_TOKEN: $FLOWAPI_TOKEN - networks: - - api \ No newline at end of file diff --git a/docs/source/administrator/deployment.md b/docs/source/administrator/deployment.md index 9e7237d1c5..877c793d0a 100644 --- a/docs/source/administrator/deployment.md +++ b/docs/source/administrator/deployment.md @@ -314,28 +314,6 @@ conn = flowclient.Connection(url="https://localhost:9090", token="JWT_STRING", s (This generates a certificate valid for the `flow.api` domain as well, which you can use by adding a corresponding entry to your `/etc/hosts` file.) - -#### AutoFlow production deployment - -Analysts with permission to run docker containers may choose to run their own AutoFlow instances. Instructions for doing so can be found in the [AutoFlow documentation](../analyst/autoflow.md#running-autoflow). A sample stack file for deploying AutoFlow along with the rest of the FlowKit stack can be found [here](https://github.com/Flowminder/FlowKit/blob/master/autoflow/docker-stack.yml), which adds an AutoFlow service, and an additionl Postgres database used by AutoFlow to record workflow runs. This makes use of the `cert-flowkit.pem` secret provided to FlowAPI, and also requires two other secrets: - -| Secret name | Secret purpose | -| ----------- | -------------- | -| AUTOFLOW_DB_PASSWORD | Password for AutoFlow's database | -| FLOWAPI_TOKEN | API token AutoFlow will use to connect to FlowAPI | - -You should also set the following environment variables: - -| Variable name | Purpose | -| ------------- | ------- | -| AUTOFLOW_INPUTS_DIR | Path on the host to the directory where input files to AutoFlow are stored | -| AUTOFLOW_OUTPUTS_DIR | Path on the host to a directory where AutoFLow should store output files | - -and optionally set the `AUTOFLOW_LOG_LEVEL` environment variable (default 'ERROR'). - -!!!note - AutoFlow input files (Jupyter notebooks and `workflows.yml`) should be in the inputs directory before starting the AutoFlow container. Files added later will not be picked up by AutoFlow. - #### Demonstrating successful deployment Once FlowKit installation is complete, you can verify that the system has been successfully set up by visiting `http://:/api/0/spec/redoc`. Once all the services have come up, you will be able to view the interactive API specification. diff --git a/docs/source/analyst/autoflow.md b/docs/source/analyst/autoflow.md deleted file mode 100644 index 41175234ce..0000000000 --- a/docs/source/analyst/autoflow.md +++ /dev/null @@ -1,184 +0,0 @@ -# AutoFlow - -AutoFlow is a tool that automates the event-driven execution of workflows consisting of Jupyter notebooks that interact with FlowKit via FlowAPI. Workflows can consist of multiple inter-dependent notebooks, and can run automatically for each new date of CDR data available in FlowDB. After execution, notebooks can optionally be converted to PDF reports. - -AutoFlow consists of a sensor that, on a user-defined schedule, checks for new available dates of CDR data in a FlowKit instance, and runs a set of notebook-based workflows for each date for which they haven't previously run successfully. Each notebook-based workflow is a user-defined workflow that executes one or more Jupyter notebooks, and optionally converts the executed notebooks to PDF. - -## Running AutoFlow - -AutoFlow is designed to run in a Docker container (defined by the `flowminder/autoflow` image) with two bind-mounted directories: - -- `/mounts/inputs`: Inputs directory (read-only), which contains the notebooks to be executed along with a YAML file `workflows.yml` that defines the workflows to execute the notebooks and configures the available dates sensor to run these workflows. See the [inputs section](#inputs) for details of what this directory should contain, and see the [example section](#example) for an example. -- `/mounts/outputs`: Outputs directory, which will contain two sub-directories `notebooks/` and `reports/` containing the executed notebooks and PDF reports, respectively. The sub-directories will be created if they do not already exist. - -When running the container, the following environment variables should be set: - -- `AUTOFLOW_LOG_LEVEL`: Logging level (e.g. 'DEBUG' or 'ERROR'). Must be upper-case. -- `AUTOFLOW_DB_URI`: URI of a SQL database in which AutoFlow will store metadata for workflow runs. The password can be replaced by `'{}'`, e.g. `postgresql://user:{}@localhost:5432/autoflow`. If database URI is not provided, a temporary SQLite database will be created inside the container. -- `AUTOFLOW_DB_PASSWORD`: Password for the database. -- `FLOWAPI_URL`: FlowAPI URL. -- `FLOWAPI_TOKEN`: FlowAPI access token. This should allow access to `available_dates`, along with any query kinds used in the notebooks to be executed. - -Alternatively, `AUTOFLOW_DB_PASSWORD` and `FLOWAPI_TOKEN` can be set as docker secrets instead of environment variables. If connecting to FlowAPI over a secure connection (i.e. if `FLOWAPI_URL` starts with 'https://'), then the `SSL_CERTIFICATE_FILE` environment variable should be set as the path (within the container) to the SSL certificate for FlowAPI. For example, if the SSL certificate file is set as the Docker secret `cert.pem`, then `SSL_CERTIFICATE_FILE` should be set as `"/run/secrets/cert.pem"`. - -A sample Docker stack file for deploying AutoFlow along with a Postgres databaase can be found [here](https://github.com/Flowminder/FlowKit/blob/master/autoflow/docker-stack.yml). See the [deployment documentation](../administrator/deployment.md#autoflow-production-deployment) for details on using this file. - -When the container runs, it will parse `workflows.yml` and construct all of the workflows defined, and run a sensor that runs the workflows with the provided parameters each time new data is found in FlowDB. All executed notebooks will be available in `/notebooks/`, and PDF reports will be in `/reports/`. - -## Inputs - -### Defining workflows - -Workflows should be defined in a yaml file `workflows.yml`, in the inputs directory that will be bind-mounted into the AutoFlow container. This file should have the following structure: - -- `workflows`: A sequence of workflow specifications. Each workflow specification has the following keys: - - `name`: A unique name for this workflow. - - `notebooks`: Specifications for one or more notebook execution tasks, defined as a mapping from labels to notebook task specifications. See the section on [defining notebook tasks](#defining-notebook-tasks) for more details. -- `available_dates_sensor`: Configuration parameters for the available dates sensor. This should have the following parameters: - - `schedule`: A cron string describing the schedule on which the sensor will check for new available dates. Set `schedule: null` to run the sensor just once, without a schedule. - - `cdr_types` (optional): A list of CDR types for which available dates should be checked (can be any subset of ["calls", "sms", "mds", "topups"]). Omit this parameter to check available dates for all available CDR types. - - `workflows`: A list of sets of configuration parameters for workflows that the available dates sensor should trigger. Each element of this list should have the following parameters: - - `workflow_name`: The name of a workflow defined in the `workflows` section at the top of `workflows.yml`. - - `parameters`: Values of any parameters used by notebooks in the workflow (except `reference_date`, `date_ranges` and `flowapi_url`, which will be provided automatically). - - `earliest_date` (optional): Optionally specify the earliest date of available data for which this workflow should run. - - `date_stencil` (optional): Optionally provide a date stencil that defines a set of dates that must be available for this workflow to run. See the section on [date stencils](#date-stencils) for more details. - -See the [example section](#example) for an example. - -#### Defining notebook tasks - -Jupyter notebooks in the inputs directory can be executed as tasks within a workflow. The value of the `notebooks` parameter within a workflow specification in `workflows.yml` should be a mapping from notebook task keys (which notebook tasks can use to refer to each other) to notebook task specifications. Each notebook task specification has the following keys: - -- `filename`: Filename of the Jupyter notebook to be executed. This should refer to a file in the same directory as `workflows.yml`. -- `parameters`: A mapping that defines parameters for this notebook. Keys are the names of these parameters inside the notebook, and each value refers to one of: - - The name of one of the parameters of this workflow. - - The key of another notebook task in this workflow (i.e. the key corresponding to a notebook task specification). Dependencies between notebook tasks can be defined in this way, and these dependencies will be respected by the order in which notebooks are executed. The value of this parameter within the notebook will be the filename of the other notebook after execution. - - The name of an 'automatic' workflow parameter (i.e. a parameter that will automatically be passed by the available dates sensor). There are two 'automatic' parameters: - - `reference_date`: The date of available CDR data for which the workflow is running (as an iso-format date string). - - `date_ranges`: A list of pairs of iso-format date strings, describing the date ranges defined by the `date_stencil` (see the section on [date stencils](#date-stencils) below) for `reference_date`. - - `flowapi_url`: The URL at which FlowAPI can be accessed. The value of this parameter will be the URL set as the environment variable `FLOWAPI_URL` inside the container. -- `output` (optional): Set `output: {format: pdf}` to convert this notebook to PDF after execution. Omit the `output` key to skip converting this notebook to PDF. Optionally, a custom template can be used when converting the notebook to asciidoc by setting `output: {format: pdf, template: custom_asciidoc_template.tpl}` (where `custom_asciidoc_template.tpl` should be a file in the same directory as `workflows.yml`). - -#### Date stencils - -The default behaviour is to run a workflow for every date of available CDR data. If the notebooks refer to date periods other than the reference date for which the workflow is running, further filtering is required to ensure that all required dates are available. This can be done by providing a date stencil. - -Each element of the list of workflows defined in the `available_dates_sensor` block in `workflows.yml` accepts a `date_stencil` parameter, which defines a pattern of dates relative to each available CDR date. The workflow will only run for a particular date if all dates contained in the date stencil for that date are available. The date stencil is a list of elements, each of which is one of: - -- an absolute date (e.g.`2016-01-01`); -- an integer representing an offset (in days) from a reference date. For example, `-1` would refer to the day before a reference date, so a workflow containing `-1` would only run for date `2016-01-03` if date `2016-01-02` was available; -- a pair of dates/offsets representing a half-open date interval (inclusive of lower limit) for which all dates must be available. Absolute dates and offsets can be mixed (e.g. `[2016-01-01, -3]`). - -If the `date_stencil` parameter is not provided, the default value is `[0]` (i.e. only the reference date itself needs to be available). - -For each date for which the workflow runs, the corresponding date ranges described by the date stencil are available to notebooks as the parameter `date_ranges`, which will contain a list of pairs of absolute dates. Note that `date_ranges` will always contain pairs of dates, even when the date stencil contains individual dates/offsets. - -As an example, if the date stencil is -```yaml -date_stencil: - - [2016-01-01, 2016-01-03] - - -3 - - [-2, 0] -``` -then the internal `date_ranges` parameter for reference date `2016-01-07` would have the value -```python -[ - ["2016-01-01", "2016-01-03"], - ["2016-01-04", "2016-01-05"], - ["2016-01-05", "2016-01-07"], -] -``` -and the workflow will only run for reference date `2016-01-07` if all of the dates `2016-01-01`, `2016-01-02`, `2016-01-04`, `2016-01-05` and `2016-01-06` are available. - -### Preparing notebooks for automated execution - -#### Parameters - -Notebooks are executed within workflows using [papermill](https://github.com/nteract/papermill), which enables passing parameters into a notebook. Within a notebook, indicate the cell containing parameters that should be replaced at execution time by adding the tag `parameters` to that cell (see [papermill's documentation](https://papermill.readthedocs.io/en/latest/usage-parameterize.html) for more details). - -#### Passing data between notebooks - -[Scrapbook](https://github.com/nteract/scrapbook) can be used to attach data to a notebook during execution, so that it can later be retrieved in another notebook. - -To 'glue' the value of a variable `x` in a notebook: -```python -import scrapbook as sb -sb.glue("scrap_name", x) -``` -This value can be retrieved in another notebook: -```python -import scrapbook as sb -nb = sb.read_notebook(path_to_first_notebook) -x = nb.scraps["scrap_name"].data -``` - -See [scrapbook's documentation](https://nteract-scrapbook.readthedocs.io/en/latest/index.html) for more details. - -As an example, if the `notebooks` parameter of a workflow specification is -```yaml -notebooks: - notebook1: - filename: ... - parameters: ... - notebook2: - filename: ... - parameters: - path_to_first_notebook: notebook1 - ... -``` -then the `path_to_first_notebook` parameter in `notebook2` will be the path to the executed `notebook1`, so values 'glued' in `notebook1` can be accessed in `notebook2` using `sb.read_notebook(path_to_first_notebook)`. - -#### Connecting to FlowAPI - -Currently, the best way to create a FlowClient connection in a notebook is to pass the parameter `flowapi_url` to the notebook, and then run -```python -import flowclient -from get_secret_or_env_var import environ, getenv -conn = flowclient.connect( - url=flowapi_url, - token=environ["FLOWAPI_TOKEN"], - ssl_certificate=getenv("SSL_CERTIFICATE_FILE"), -) -``` - -!!! note - This method for accessing the FlowAPI access token is likely to change in the future. - -The example notebooks in the [example section](#example) contain examples of parameterisation with papermill, data-sharing with scrapbook, and creating a FlowClient connection. - -## Example - -[This directory](https://github.com/Flowminder/FlowKit/tree/master/autoflow/examples/inputs) contains example inputs for an AutoFlow workflow that runs two Jupyter notebooks and produces a PDF report showing in/outflows above normal for each day of CDR data. It contains two Jupyter notebooks: - -- `run_flows.ipynb`: Notebook that runs two `flows` queries, so that they are stored in cache. The query IDs for the `flows` queries are glued in this notebook. -- `flows_report.ipynb`: Notebook that reads the query IDs from `run_flows.ipynb`, gets the query results using FlowClient and combines them with geography data, and displays the flows above normal in plots and tables. This notebook is designed to be converted to a PDF report after execution. - -and an input file `workflows.yml` that defines a workflow that runs `run_flows.ipynb` followed by `flows_report.ipynb` and creates a PDF report from `flows_report.ipynb`, and configures the available dates sensor to check for new dates every day at midnight, and run the workflow with two different sets of parameters. - -To run the AutoFlow example: - -1. Start a FlowKit instance, by following the [quick-start instructions](../install.md#quickinstall). -2. Go to FlowAuth, log in with username `TEST_USER` and password `DUMMY_PASSWORD`, and create an API token following instructions [here](index.md#flowauth). -3. Create two empty directories, one for inputs and the other for outputs. -4. Copy the example input files into your inputs directory: -```bash -cd -wget https://raw.githubusercontent.com/Flowminder/FlowKit/master/autoflow/examples/inputs/run_flows.ipynb -wget https://raw.githubusercontent.com/Flowminder/FlowKit/master/autoflow/examples/inputs/flows_report.ipynb -wget https://raw.githubusercontent.com/Flowminder/FlowKit/master/autoflow/examples/inputs/workflows.yml -``` -5. Start AutoFlow: -```bash -docker run -d \ - -v :/mounts/inputs:ro \ - -v :/mounts/outputs:rw \ - -e "AUTOFLOW_LOG_LEVEL=ERROR" \ - -e "FLOWAPI_URL=http://localhost:9090" \ - -e "FLOWAPI_TOKEN=" \ - flowminder/autoflow:latest -``` -(where `` and `` are the paths to the input and output directories you created, and `` is the token you created in FlowAuth). - -To run the sensor immediately without a schedule, rather than waiting until midnight, change the line `schedule: "0 0 * * *"` to `schedule: null` in `workflows.yml`. - -Outputs will appear in the outputs directory you created. diff --git a/docs/source/analyst/index.md b/docs/source/analyst/index.md index 46582c5c07..683ed28da6 100644 --- a/docs/source/analyst/index.md +++ b/docs/source/analyst/index.md @@ -30,10 +30,6 @@ To connect FlowClient to FlowAPI, an access token must be generated using FlowAu 5. Click "COPY" to copy the token string, "DOWNLOAD" to download the token as a text file, or "VIEW" to display the token string. -## AutoFlow - -AutoFlow is a tool to automate workflows consisting of Jupyter notebooks that interact with FlowKit. Use-cases include automatically producing a PDF report for each day of CDR data available in a FlowKit instance. Documentation for AutoFlow can be found [here](autoflow.md). - ## FlowAPI Advanced users may wish to write their own clients that interface directly to FlowAPI. This is discussed in more detail in the [Developer](../developer/index.md) section of these documents. diff --git a/docs/source/developer/dev_environment_setup.md b/docs/source/developer/dev_environment_setup.md index 899b161592..60eb3e013d 100644 --- a/docs/source/developer/dev_environment_setup.md +++ b/docs/source/developer/dev_environment_setup.md @@ -11,13 +11,10 @@ During development, you will typically also want to run FlowMachine, FlowAPI, Fl - [Pipenv](https://pipenv.readthedocs.io/en/latest/) (to manage separate pipenv environment for each FlowKit component) - FlowMachine server: `Python >= 3.7` - FlowAuth: `npm` (we recommend installing it via [nvm](https://github.com/nvm-sh/nvm)); [Cypress](https://www.cypress.io/) for testing -- AutoFlow: [pandoc](https://pandoc.org/installing.html), `Ruby` (we recommend installing Ruby via [RVM](https://rvm.io/)), and [Bundler](https://bundler.io/) (to manage Ruby package dependencies). ## Setting up FlowKit for development -After cloning the [GitHub repository](https://github.com/Flowminder/FlowKit), the FlowKit system can be started by running `set -a && . development_environment && set +a` (this will set the required environment variables) followed by `make up` in the root directory. This requires [Docker](https://docs.docker.com/install/) and [Docker Compose](https://docs.docker.com/compose/install/) to be installed, and starts the FlowKit docker containers using the `docker-compose.yml` file. The docker containers to start can be selected by running `make up DOCKER_SERVICES=" ..."`, where the services can be any of `flowmachine`, `flowmachine_query_locker`, `flowapi`, `flowauth`, `flowdb`, `worked_examples`, `flowdb_testdata`, `flowdb_synthetic_data`, `flowetl`, `flowetl_db` or `autoflow` (at most one of the `flowdb` containers). The default is to start `flowdb`, `flowapi`, `flowmachine`, `flowauth`, `flowmachine_query_locker`, `flowetl`, `flowetl_db` and `worked_examples`. Alternatively, containers can be built, started or stopped individually by running `make -build`, `make -up` or `make -down`, respectively. - -To run the `autoflow` service, a valid FlowAPI token must be set as the environment variable `FLOWAPI_TOKEN` (in addition to the environment variables set by running `set -a && . development_environment && set +a`). +After cloning the [GitHub repository](https://github.com/Flowminder/FlowKit), the FlowKit system can be started by running `set -a && . development_environment && set +a` (this will set the required environment variables) followed by `make up` in the root directory. This requires [Docker](https://docs.docker.com/install/) and [Docker Compose](https://docs.docker.com/compose/install/) to be installed, and starts the FlowKit docker containers using the `docker-compose.yml` file. The docker containers to start can be selected by running `make up DOCKER_SERVICES=" ..."`, where the services can be any of `flowmachine`, `flowmachine_query_locker`, `flowapi`, `flowauth`, `flowdb`, `worked_examples`, `flowdb_testdata`, `flowdb_synthetic_data`, `flowetl` or `flowetl_db` (at most one of the `flowdb` containers). The default is to start `flowdb`, `flowapi`, `flowmachine`, `flowauth`, `flowmachine_query_locker`, `flowetl`, `flowetl_db` and `worked_examples`. Alternatively, containers can be built, started or stopped individually by running `make -build`, `make -up` or `make -down`, respectively. FlowKit uses [pipenv](https://pipenv.readthedocs.io/) to manage Python environments. To start a Python session in which you can use FlowClient: @@ -28,7 +25,7 @@ pipenv run python >>> import flowclient ``` -To run the tests in the `flowapi`, `flowclient`, `flowdb`, `flowmachine`, `autoflow`, `flowetl` or `integration_tests` directory: +To run the tests in the `flowapi`, `flowclient`, `flowdb`, `flowmachine`, `flowetl` or `integration_tests` directory: ```bash cd @@ -36,15 +33,6 @@ pipenv install --dev pipenv run pytest ``` -AutoFlow additionally has dependencies on Ruby packages, which we manage using [Bundler](https://bundler.io/) which works similarly to pipenv. To run the tests in the `autoflow` directory: - -```bash -cd autoflow -bundle install -pipenv install --dev -pipenv run pytest -``` - ## Getting set up to contribute diff --git a/integration_tests/Gemfile b/integration_tests/Gemfile deleted file mode 120000 index d0a7289a8d..0000000000 --- a/integration_tests/Gemfile +++ /dev/null @@ -1 +0,0 @@ -../autoflow/Gemfile \ No newline at end of file diff --git a/integration_tests/Gemfile.lock b/integration_tests/Gemfile.lock deleted file mode 120000 index 8ef3a64b01..0000000000 --- a/integration_tests/Gemfile.lock +++ /dev/null @@ -1 +0,0 @@ -../autoflow/Gemfile.lock \ No newline at end of file diff --git a/integration_tests/Pipfile b/integration_tests/Pipfile index a3d9cafceb..70dba0cd0e 100644 --- a/integration_tests/Pipfile +++ b/integration_tests/Pipfile @@ -4,7 +4,6 @@ verify_ssl = true name = "pypi" [packages] -autoflow = {editable = true,path = "./../autoflow",extras = ["examples"]} black = {extras = ["jupyter"],version = "==23.7.0"} # required by papermill pytest = "<7.3.0" # https://github.com/taverntesting/tavern/issues/859 pytest-asyncio = "*" diff --git a/integration_tests/tests/autoflow_tests/test_example.py b/integration_tests/tests/autoflow_tests/test_example.py deleted file mode 100644 index 4f65e35275..0000000000 --- a/integration_tests/tests/autoflow_tests/test_example.py +++ /dev/null @@ -1,82 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import pytest - -import shutil - -from sqlalchemy import create_engine -from sqlalchemy.ext.automap import automap_base -from sqlalchemy.orm import Session -from pathlib import Path - - -def test_autoflow_example(monkeypatch, tmp_path, flowapi_url, universal_access_token): - """ - Test that the AutoFlow example runs correctly and produces the expected output. - """ - # Set up files and directories - config_dir = tmp_path / "config" - inputs_dir = tmp_path / "inputs" - outputs_dir = tmp_path / "outputs" - shutil.copytree( - Path(__file__).parent.parent.parent.parent / "autoflow" / "config", config_dir - ) - shutil.copytree( - Path(__file__).parent.parent.parent.parent / "autoflow" / "examples" / "inputs", - inputs_dir, - ) - outputs_dir.mkdir() - # Database URI - db_uri = f"sqlite:///{str(tmp_path/'test.db')}" - # Set environment variables - monkeypatch.setenv("AUTOFLOW_INPUTS_DIR", str(inputs_dir)) - monkeypatch.setenv("AUTOFLOW_OUTPUTS_DIR", str(outputs_dir)) - monkeypatch.setenv("AUTOFLOW_LOG_LEVEL", "DEBUG") - monkeypatch.setenv("AUTOFLOW_DB_URI", db_uri) - monkeypatch.setenv("FLOWAPI_URL", flowapi_url) - monkeypatch.setenv("FLOWAPI_TOKEN", universal_access_token) - monkeypatch.setenv("PREFECT__USER_CONFIG_PATH", str(config_dir / "config.toml")) - monkeypatch.setenv( - "PREFECT__ASCIIDOC_TEMPLATE_PATH", str(config_dir / "asciidoc_extended.tpl") - ) - # Import autoflow (this must be done after setting the environment variables, - # otherwise they won't be picked up in the prefect config) - from autoflow import app, sensor - - # Run AutoFlow - app.main(run_on_schedule=False) - - # Test that the sensor has the correct schedule - assert sensor.available_dates_sensor.schedule.clocks[0].cron == "0 0 * * *" - # Test that the expected files have been created in outputs_dir - expected_files = [ - "notebooks/run_flows__flows_above_normal_2016-01-0{}_*__*Z.ipynb", - "notebooks/flows_report__flows_above_normal_2016-01-0{}_*__*Z.ipynb", - "reports/flows_report__flows_above_normal_2016-01-0{}_*__*Z.pdf", - ] - expected_file_counts = [0, 0, 1, 2, 2, 2, 2] - for file_pattern in expected_files: - assert [ - len(list(outputs_dir.glob(file_pattern.format(d)))) for d in range(1, 8) - ] == expected_file_counts - # Check the content of the database - Base = automap_base() - engine = create_engine(db_uri) - Base.prepare(engine, reflect=True) - assert "workflow_runs" in Base.metadata.tables - session = Session(engine) - assert ( - len(session.query(Base.classes.workflow_runs).filter_by(state="running").all()) - == 9 - ) - assert ( - len(session.query(Base.classes.workflow_runs).filter_by(state="success").all()) - == 9 - ) - assert ( - len(session.query(Base.classes.workflow_runs).filter_by(state="failed").all()) - == 0 - ) - session.close() From 28b735a894ec4e96d38aafcaa608a001aad27ee5 Mon Sep 17 00:00:00 2001 From: James Harrison Date: Wed, 8 Nov 2023 12:32:04 +0000 Subject: [PATCH 2/7] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc888a9393..d4610894cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Removed +- Removed AutoFlow. [#6394](https://github.com/Flowminder/FlowKit/issues/6394) + ## [1.19.0] ### Added From 45ffc42d79cd565bffaac063019afc7cc88ae247 Mon Sep 17 00:00:00 2001 From: James Harrison Date: Wed, 8 Nov 2023 12:38:33 +0000 Subject: [PATCH 3/7] Remove AutoFlow dependabot update --- .github/dependabot.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f55ee91367..b22ca32295 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,15 +11,6 @@ updates: open-pull-requests-limit: 10 ignore: - dependency-name: "apache/airflow" # Airflow image version needs to stay in sync with version in flowetl Pipfile - - package-ecosystem: bundler - directory: "/autoflow" - schedule: - interval: daily - time: "04:00" - timezone: Europe/London - pull-request-branch-name: - separator: "-" - open-pull-requests-limit: 10 - package-ecosystem: npm directory: "/flowauth/frontend" schedule: From facde350930977d7d8c0b780f4e4f2198ec54e38 Mon Sep 17 00:00:00 2001 From: James Harrison Date: Wed, 8 Nov 2023 12:54:24 +0000 Subject: [PATCH 4/7] Re-lock integration tests Pipfile --- integration_tests/Pipfile.lock | 1734 +++++++------------------------- 1 file changed, 378 insertions(+), 1356 deletions(-) diff --git a/integration_tests/Pipfile.lock b/integration_tests/Pipfile.lock index 196385271f..c8b548eaf0 100644 --- a/integration_tests/Pipfile.lock +++ b/integration_tests/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "fca635829831e2507939b3d97760f1dcaf5b51439949a3d94e003f571a75755d" + "sha256": "daf92ab55fff3c9da5272b14cd0df0c382c06070831934cdaf4d846fc0982761" }, "pipfile-spec": 6, "requires": { @@ -32,13 +32,6 @@ "markers": "python_version >= '3.7'", "version": "==2.5.1" }, - "ansiwrap": { - "hashes": [ - "sha256:7b053567c88e1ad9eed030d3ac41b722125e4c1271c8a99ade797faff1f49fb1", - "sha256:ca0c740734cde59bf919f8ff2c386f74f9a369818cdc60efe94893d01ea8d9b7" - ], - "version": "==0.8.4" - }, "anyio": { "hashes": [ "sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f", @@ -102,54 +95,55 @@ "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f", "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028" ], - "markers": "python_full_version <= '3.11.2'", + "markers": "python_full_version < '3.12.0'", "version": "==4.0.3" }, "asyncpg": { "hashes": [ - "sha256:0740f836985fd2bd73dca42c50c6074d1d61376e134d7ad3ad7566c4f79f8184", - "sha256:0a6d1b954d2b296292ddff4e0060f494bb4270d87fb3655dd23c5c6096d16d83", - "sha256:0c402745185414e4c204a02daca3d22d732b37359db4d2e705172324e2d94e85", - "sha256:1c56092465e718a9fdcc726cc3d9dcf3a692e4834031c9a9f871d92a75d20d48", - "sha256:319f5fa1ab0432bc91fb39b3960b0d591e6b5c7844dafc92c79e3f1bff96abef", - "sha256:3ed77f00c6aacfe9d79e9eff9e21729ce92a4b38e80ea99a58ed382f42ebd55b", - "sha256:41e97248d9076bc8e4849da9e33e051be7ba37cd507cbd51dfe4b2d99c70e3dc", - "sha256:4acd6830a7da0eb4426249d71353e8895b350daae2380cb26d11e0d4a01c5472", - "sha256:4d32b680a9b16d2957a0a3cc6b7fa39068baba8e6b728f2e0a148a67644578f4", - "sha256:4f20cac332c2576c79c2e8e6464791c1f1628416d1115935a34ddd7121bfc6a4", - "sha256:59f9712ce01e146ff71d95d561fb68bd2d588a35a187116ef05028675462d5ed", - "sha256:5e18438a0730d1c0c1715016eacda6e9a505fc5aa931b37c97d928d44941b4bf", - "sha256:5e7337c98fb493079d686a4a6965e8bcb059b8e1b8ec42106322fc6c1c889bb0", - "sha256:63861bb4a540fa033a56db3bb58b0c128c56fad5d24e6d0a8c37cb29b17c1c7d", - "sha256:7252cdc3acb2f52feaa3664280d3bcd78a46bd6c10bfd681acfffefa1120e278", - "sha256:76aacdcd5e2e9999e83c8fbcb748208b60925cc714a578925adcb446d709016c", - "sha256:7b48ceed606cce9e64fd5480a9b0b9a95cea2b798bb95129687abd8599c8b019", - "sha256:86b339984d55e8202e0c4b252e9573e26e5afa05617ed02252544f7b3e6de3e9", - "sha256:8858f713810f4fe67876728680f42e93b7e7d5c7b61cf2118ef9153ec16b9423", - "sha256:8aec08e7310f9ab322925ae5c768532e1d78cfb6440f63c078b8392a38aa636a", - "sha256:8ba7d06a0bea539e0487234511d4adf81dc8762249858ed2a580534e1720db00", - "sha256:90a7bae882a9e65a9e448fdad3e090c2609bb4637d2a9c90bfdcebbfc334bf89", - "sha256:99417210461a41891c4ff301490a8713d1ca99b694fef05dabd7139f9d64bd6c", - "sha256:9e721dccd3838fcff66da98709ed884df1e30a95f6ba19f595a3706b4bc757e3", - "sha256:a0e08fe2c9b3618459caaef35979d45f4e4f8d4f79490c9fa3367251366af207", - "sha256:a93a94ae777c70772073d0512f21c74ac82a8a49be3a1d982e3f259ab5f27307", - "sha256:ad1d6abf6c2f5152f46fff06b0e74f25800ce8ec6c80967f0bc789974de3c652", - "sha256:b24e521f6060ff5d35f761a623b0042c84b9c9b9fb82786aadca95a9cb4a893b", - "sha256:b337ededaabc91c26bf577bfcd19b5508d879c0ad009722be5bb0a9dd30b85a0", - "sha256:c88eef5e096296626e9688f00ab627231f709d0e7e3fb84bb4413dff81d996d7", - "sha256:d009b08602b8b18edef3a731f2ce6d3f57d8dac2a0a4140367e194eabd3de457", - "sha256:d14681110e51a9bc9c065c4e7944e8139076a778e56d6f6a306a26e740ed86d2", - "sha256:d7fa81ada2807bc50fea1dc741b26a4e99258825ba55913b0ddbf199a10d69d8", - "sha256:e907cf620a819fab1737f2dd90c0f185e2a796f139ac7de6aa3212a8af96c050", - "sha256:e9c433f6fcdd61c21a715ee9128a3ca48be8ac16fa07be69262f016bb0f4dbd2", - "sha256:ec46a58d81446d580fb21b376ec6baecab7288ce5a578943e2fc7ab73bf7eb39", - "sha256:f029c5adf08c47b10bcdc857001bbef551ae51c57b3110964844a9d79ca0f267", - "sha256:f33c5685e97821533df3ada9384e7784bd1e7865d2b22f153f2e4bd4a083e102", - "sha256:f4f62f04cdf38441a70f279505ef3b4eadf64479b17e707c950515846a2df197", - "sha256:fc9e9f9ff1aa0eddcc3247a180ac9e9b51a62311e988809ac6152e8fb8097756" + "sha256:0009a300cae37b8c525e5b449233d59cd9868fd35431abc470a3e364d2b85cb9", + "sha256:000c996c53c04770798053e1730d34e30cb645ad95a63265aec82da9093d88e7", + "sha256:012d01df61e009015944ac7543d6ee30c2dc1eb2f6b10b62a3f598beb6531548", + "sha256:039a261af4f38f949095e1e780bae84a25ffe3e370175193174eb08d3cecab23", + "sha256:103aad2b92d1506700cbf51cd8bb5441e7e72e87a7b3a2ca4e32c840f051a6a3", + "sha256:1e186427c88225ef730555f5fdda6c1812daa884064bfe6bc462fd3a71c4b675", + "sha256:2245be8ec5047a605e0b454c894e54bf2ec787ac04b1cb7e0d3c67aa1e32f0fe", + "sha256:37a2ec1b9ff88d8773d3eb6d3784dc7e3fee7756a5317b67f923172a4748a175", + "sha256:48e7c58b516057126b363cec8ca02b804644fd012ef8e6c7e23386b7d5e6ce83", + "sha256:52e8f8f9ff6e21f9b39ca9f8e3e33a5fcdceaf5667a8c5c32bee158e313be385", + "sha256:5340dd515d7e52f4c11ada32171d87c05570479dc01dc66d03ee3e150fb695da", + "sha256:54858bc25b49d1114178d65a88e48ad50cb2b6f3e475caa0f0c092d5f527c106", + "sha256:5b52e46f165585fd6af4863f268566668407c76b2c72d366bb8b522fa66f1870", + "sha256:5bbb7f2cafd8d1fa3e65431833de2642f4b2124be61a449fa064e1a08d27e449", + "sha256:5cad1324dbb33f3ca0cd2074d5114354ed3be2b94d48ddfd88af75ebda7c43cc", + "sha256:6011b0dc29886ab424dc042bf9eeb507670a3b40aece3439944006aafe023178", + "sha256:642a36eb41b6313ffa328e8a5c5c2b5bea6ee138546c9c3cf1bffaad8ee36dd9", + "sha256:6feaf2d8f9138d190e5ec4390c1715c3e87b37715cd69b2c3dfca616134efd2b", + "sha256:72fd0ef9f00aeed37179c62282a3d14262dbbafb74ec0ba16e1b1864d8a12169", + "sha256:746e80d83ad5d5464cfbf94315eb6744222ab00aa4e522b704322fb182b83610", + "sha256:76c3ac6530904838a4b650b2880f8e7af938ee049e769ec2fba7cd66469d7772", + "sha256:797ab8123ebaed304a1fad4d7576d5376c3a006a4100380fb9d517f0b59c1ab2", + "sha256:8d36c7f14a22ec9e928f15f92a48207546ffe68bc412f3be718eedccdf10dc5c", + "sha256:97eb024685b1d7e72b1972863de527c11ff87960837919dac6e34754768098eb", + "sha256:a65c1dcd820d5aea7c7d82a3fdcb70e096f8f70d1a8bf93eb458e49bfad036ac", + "sha256:a921372bbd0aa3a5822dd0409da61b4cd50df89ae85150149f8c119f23e8c408", + "sha256:a9e6823a7012be8b68301342ba33b4740e5a166f6bbda0aee32bc01638491a22", + "sha256:b544ffc66b039d5ec5a7454667f855f7fec08e0dfaf5a5490dfafbb7abbd2cfb", + "sha256:bb1292d9fad43112a85e98ecdc2e051602bce97c199920586be83254d9dafc02", + "sha256:bde17a1861cf10d5afce80a36fca736a86769ab3579532c03e45f83ba8a09c59", + "sha256:cce08a178858b426ae1aa8409b5cc171def45d4293626e7aa6510696d46decd8", + "sha256:cfe73ffae35f518cfd6e4e5f5abb2618ceb5ef02a2365ce64f132601000587d3", + "sha256:d1c49e1f44fffafd9a55e1a9b101590859d881d639ea2922516f5d9c512d354e", + "sha256:d4900ee08e85af01adb207519bb4e14b1cae8fd21e0ccf80fac6aa60b6da37b4", + "sha256:d84156d5fb530b06c493f9e7635aa18f518fa1d1395ef240d211cb563c4e2364", + "sha256:dc600ee8ef3dd38b8d67421359779f8ccec30b463e7aec7ed481c8346decf99f", + "sha256:e0bfe9c4d3429706cf70d3249089de14d6a01192d617e9093a8e941fea8ee775", + "sha256:e17b52c6cf83e170d3d865571ba574577ab8e533e7361a2b8ce6157d02c665d3", + "sha256:f100d23f273555f4b19b74a96840aa27b85e99ba4b1f18d4ebff0734e78dc090", + "sha256:f9ea3f24eb4c49a615573724d88a48bd1b7821c890c2effe04f05382ed9e8810", + "sha256:ff8e8109cd6a46ff852a5e6bab8b0a047d7ea42fcb7ca5ae6eaae97d8eacf397" ], - "markers": "python_full_version >= '3.7.0'", - "version": "==0.28.0" + "markers": "python_full_version >= '3.8.0'", + "version": "==0.29.0" }, "attrs": { "hashes": [ @@ -159,13 +153,6 @@ "markers": "python_version >= '3.7'", "version": "==23.1.0" }, - "autoflow": { - "editable": true, - "extras": [ - "examples" - ], - "path": "./../autoflow" - }, "backcall": { "hashes": [ "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e", @@ -212,21 +199,13 @@ "markers": "python_version >= '3.8'", "version": "==23.7.0" }, - "bleach": { - "hashes": [ - "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe", - "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6" - ], - "markers": "python_version >= '3.8'", - "version": "==6.1.0" - }, "blinker": { "hashes": [ - "sha256:152090d27c1c5c722ee7e48504b02d76502811ce02e1523553b4cf8c8b3d3a8d", - "sha256:296320d6c28b006eb5e32d4712202dbcdcbf5dc482da298c2f44881c43884aaa" + "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", + "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" ], - "markers": "python_version >= '3.7'", - "version": "==1.6.3" + "markers": "python_version >= '3.8'", + "version": "==1.7.0" }, "cachetools": { "hashes": [ @@ -312,99 +291,99 @@ }, "charset-normalizer": { "hashes": [ - "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5", - "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93", - "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a", - "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d", - "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c", - "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1", - "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58", - "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2", - "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557", - "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147", - "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041", - "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2", - "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2", - "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7", - "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296", - "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690", - "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67", - "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57", - "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597", - "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846", - "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b", - "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97", - "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c", - "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62", - "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa", - "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f", - "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e", - "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821", - "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3", - "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4", - "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb", - "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727", - "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514", - "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d", - "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761", - "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55", - "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f", - "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c", - "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034", - "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6", - "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae", - "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1", - "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14", - "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1", - "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228", - "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708", - "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48", - "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f", - "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5", - "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f", - "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4", - "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8", - "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff", - "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61", - "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b", - "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97", - "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b", - "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605", - "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728", - "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d", - "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c", - "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf", - "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673", - "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1", - "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b", - "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41", - "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8", - "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f", - "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4", - "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008", - "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9", - "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5", - "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f", - "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e", - "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273", - "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45", - "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e", - "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656", - "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e", - "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c", - "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2", - "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72", - "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056", - "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397", - "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42", - "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd", - "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3", - "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213", - "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf", - "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67" + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.1" + "version": "==3.3.2" }, "click": { "hashes": [ @@ -414,94 +393,13 @@ "markers": "python_version >= '3.7'", "version": "==8.1.7" }, - "click-plugins": { - "hashes": [ - "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b", - "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8" - ], - "version": "==1.1.1" - }, - "cligj": { - "hashes": [ - "sha256:a4bc13d623356b373c2c27c53dbd9c68cae5d526270bfa71f6c6fa69669c6b27", - "sha256:c1ca117dbce1fe20a5809dc96f01e1c2840f6dcc939b3ddbb1111bf330ba82df" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' and python_version < '4'", - "version": "==0.7.2" - }, - "cloudpickle": { - "hashes": [ - "sha256:246ee7d0c295602a036e86369c77fecda4ab17b506496730f2f576d9016fd9c7", - "sha256:996d9a482c6fb4f33c1a35335cf8afd065d2a56e973270364840712d9131a882" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, "comm": { "hashes": [ - "sha256:354e40a59c9dd6db50c5cc6b4acc887d82e9603787f83b68c01a80a923984d15", - "sha256:6d52794cba11b36ed9860999cd10fd02d6b2eac177068fdd585e1e2f8a96e67a" - ], - "markers": "python_version >= '3.6'", - "version": "==0.1.4" - }, - "contourpy": { - "hashes": [ - "sha256:059c3d2a94b930f4dafe8105bcdc1b21de99b30b51b5bce74c753686de858cb6", - "sha256:0683e1ae20dc038075d92e0e0148f09ffcefab120e57f6b4c9c0f477ec171f33", - "sha256:07d6f11dfaf80a84c97f1a5ba50d129d9303c5b4206f776e94037332e298dda8", - "sha256:081f3c0880712e40effc5f4c3b08feca6d064cb8cfbb372ca548105b86fd6c3d", - "sha256:0e48694d6a9c5a26ee85b10130c77a011a4fedf50a7279fa0bdaf44bafb4299d", - "sha256:11b836b7dbfb74e049c302bbf74b4b8f6cb9d0b6ca1bf86cfa8ba144aedadd9c", - "sha256:19557fa407e70f20bfaba7d55b4d97b14f9480856c4fb65812e8a05fe1c6f9bf", - "sha256:229a25f68046c5cf8067d6d6351c8b99e40da11b04d8416bf8d2b1d75922521e", - "sha256:24216552104ae8f3b34120ef84825400b16eb6133af2e27a190fdc13529f023e", - "sha256:3b53d5769aa1f2d4ea407c65f2d1d08002952fac1d9e9d307aa2e1023554a163", - "sha256:3de23ca4f381c3770dee6d10ead6fff524d540c0f662e763ad1530bde5112532", - "sha256:407d864db716a067cc696d61fa1ef6637fedf03606e8417fe2aeed20a061e6b2", - "sha256:41339b24471c58dc1499e56783fedc1afa4bb018bcd035cfb0ee2ad2a7501ef8", - "sha256:462c59914dc6d81e0b11f37e560b8a7c2dbab6aca4f38be31519d442d6cde1a1", - "sha256:46e24f5412c948d81736509377e255f6040e94216bf1a9b5ea1eaa9d29f6ec1b", - "sha256:498e53573e8b94b1caeb9e62d7c2d053c263ebb6aa259c81050766beb50ff8d9", - "sha256:4ebf42695f75ee1a952f98ce9775c873e4971732a87334b099dde90b6af6a916", - "sha256:4f9147051cb8fdb29a51dc2482d792b3b23e50f8f57e3720ca2e3d438b7adf23", - "sha256:549174b0713d49871c6dee90a4b499d3f12f5e5f69641cd23c50a4542e2ca1eb", - "sha256:560f1d68a33e89c62da5da4077ba98137a5e4d3a271b29f2f195d0fba2adcb6a", - "sha256:566f0e41df06dfef2431defcfaa155f0acfa1ca4acbf8fd80895b1e7e2ada40e", - "sha256:56de98a2fb23025882a18b60c7f0ea2d2d70bbbcfcf878f9067234b1c4818442", - "sha256:66544f853bfa85c0d07a68f6c648b2ec81dafd30f272565c37ab47a33b220684", - "sha256:6c06e4c6e234fcc65435223c7b2a90f286b7f1b2733058bdf1345d218cc59e34", - "sha256:6d0a8efc258659edc5299f9ef32d8d81de8b53b45d67bf4bfa3067f31366764d", - "sha256:70e5a10f8093d228bb2b552beeb318b8928b8a94763ef03b858ef3612b29395d", - "sha256:8394e652925a18ef0091115e3cc191fef350ab6dc3cc417f06da66bf98071ae9", - "sha256:8636cd2fc5da0fb102a2504fa2c4bea3cbc149533b345d72cdf0e7a924decc45", - "sha256:93df44ab351119d14cd1e6b52a5063d3336f0754b72736cc63db59307dabb718", - "sha256:96ba37c2e24b7212a77da85004c38e7c4d155d3e72a45eeaf22c1f03f607e8ab", - "sha256:a10dab5ea1bd4401c9483450b5b0ba5416be799bbd50fc7a6cc5e2a15e03e8a3", - "sha256:a66045af6cf00e19d02191ab578a50cb93b2028c3eefed999793698e9ea768ae", - "sha256:a75cc163a5f4531a256f2c523bd80db509a49fc23721b36dd1ef2f60ff41c3cb", - "sha256:b04c2f0adaf255bf756cf08ebef1be132d3c7a06fe6f9877d55640c5e60c72c5", - "sha256:ba42e3810999a0ddd0439e6e5dbf6d034055cdc72b7c5c839f37a7c274cb4eba", - "sha256:bfc8a5e9238232a45ebc5cb3bfee71f1167064c8d382cadd6076f0d51cff1da0", - "sha256:c5bd5680f844c3ff0008523a71949a3ff5e4953eb7701b28760805bc9bcff217", - "sha256:c84fdf3da00c2827d634de4fcf17e3e067490c4aea82833625c4c8e6cdea0887", - "sha256:ca6fab080484e419528e98624fb5c4282148b847e3602dc8dbe0cb0669469887", - "sha256:d0c188ae66b772d9d61d43c6030500344c13e3f73a00d1dc241da896f379bb62", - "sha256:d6ab42f223e58b7dac1bb0af32194a7b9311065583cc75ff59dcf301afd8a431", - "sha256:dfe80c017973e6a4c367e037cb31601044dd55e6bfacd57370674867d15a899b", - "sha256:e0c02b75acfea5cab07585d25069207e478d12309557f90a61b5a3b4f77f46ce", - "sha256:e30aaf2b8a2bac57eb7e1650df1b3a4130e8d0c66fc2f861039d507a11760e1b", - "sha256:eafbef886566dc1047d7b3d4b14db0d5b7deb99638d8e1be4e23a7c7ac59ff0f", - "sha256:efe0fab26d598e1ec07d72cf03eaeeba8e42b4ecf6b9ccb5a356fde60ff08b85", - "sha256:f08e469821a5e4751c97fcd34bcb586bc243c39c2e39321822060ba902eac49e", - "sha256:f1eaac5257a8f8a047248d60e8f9315c6cff58f7803971170d952555ef6344a7", - "sha256:f29fb0b3f1217dfe9362ec55440d0743fe868497359f2cf93293f4b2701b8251", - "sha256:f44d78b61740e4e8c71db1cf1fd56d9050a4747681c59ec1094750a658ceb970", - "sha256:f6aec19457617ef468ff091669cca01fa7ea557b12b59a7908b9474bb9674cf0", - "sha256:f9dc7f933975367251c1b34da882c4f0e0b2e24bb35dc906d2f598a40b72bfc7" + "sha256:2da8d9ebb8dd7bfc247adaff99f24dce705638a8042b85cb995066793e391001", + "sha256:a517ea2ca28931c7007a7a99c562a0fa5883cfb48963140cf642c41c948498be" ], "markers": "python_version >= '3.8'", - "version": "==1.1.1" + "version": "==0.2.0" }, "coverage": { "extras": [ @@ -564,14 +462,6 @@ "markers": "python_version >= '3.8'", "version": "==7.3.2" }, - "croniter": { - "hashes": [ - "sha256:4cb064ce2d8f695b3b078be36ff50115cf8ac306c10a7e8653ee2a5b534673d7", - "sha256:d199b2ec3ea5e82988d1f72022433c5f9302b3b3ea9e6bfd6a1518f6ea5e700a" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.0.1" - }, "cryptography": { "hashes": [ "sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf", @@ -601,46 +491,6 @@ "markers": "python_version >= '3.7'", "version": "==41.0.5" }, - "cycler": { - "hashes": [ - "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", - "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c" - ], - "markers": "python_version >= '3.8'", - "version": "==0.12.1" - }, - "dask": { - "hashes": [ - "sha256:32b34986519b7ddc0947c8ca63c2fc81b964e4c208dfb5cbf9f4f8aec92d152b", - "sha256:4f4c28ac406e81b8f21b5be4b31b21308808f3e0e7c7e2f4a914f16476d9941b" - ], - "markers": "python_version >= '3.8'", - "version": "==2023.5.0" - }, - "debugpy": { - "hashes": [ - "sha256:125b9a637e013f9faac0a3d6a82bd17c8b5d2c875fb6b7e2772c5aba6d082332", - "sha256:12af2c55b419521e33d5fb21bd022df0b5eb267c3e178f1d374a63a2a6bdccd0", - "sha256:3c6fb41c98ec51dd010d7ed650accfd07a87fe5e93eca9d5f584d0578f28f35f", - "sha256:46ab6780159eeabb43c1495d9c84cf85d62975e48b6ec21ee10c95767c0590aa", - "sha256:57161629133113c97b387382045649a2b985a348f0c9366e22217c87b68b73c6", - "sha256:5d9de202f5d42e62f932507ee8b21e30d49aae7e46d5b1dd5c908db1d7068637", - "sha256:60009b132c91951354f54363f8ebdf7457aeb150e84abba5ae251b8e9f29a8a6", - "sha256:61eab4a4c8b6125d41a34bad4e5fe3d2cc145caecd63c3fe953be4cc53e65bf8", - "sha256:7fb95ca78f7ac43393cd0e0f2b6deda438ec7c5e47fa5d38553340897d2fbdfb", - "sha256:8cd0197141eb9e8a4566794550cfdcdb8b3db0818bdf8c49a8e8f8053e56e38b", - "sha256:9c9b0ac1ce2a42888199df1a1906e45e6f3c9555497643a85e0bf2406e3ffbc4", - "sha256:a64093656c4c64dc6a438e11d59369875d200bd5abb8f9b26c1f5f723622e153", - "sha256:a8b7a2fd27cd9f3553ac112f356ad4ca93338feadd8910277aff71ab24d8775f", - "sha256:b05a6b503ed520ad58c8dc682749113d2fd9f41ffd45daec16e558ca884008cd", - "sha256:bdc5ef99d14b9c0fcb35351b4fbfc06ac0ee576aeab6b2511702e5a648a2e595", - "sha256:e3412f9faa9ade82aa64a50b602544efcba848c91384e9f93497a458767e6926", - "sha256:ef54404365fae8d45cf450d0544ee40cefbcb9cb85ea7afe89a963c27028261e", - "sha256:ef9ab7df0b9a42ed9c878afd3eaaff471fce3fa73df96022e1f5c9f8f8c87ada" - ], - "markers": "python_version >= '3.8'", - "version": "==1.8.0" - }, "decorator": { "hashes": [ "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", @@ -649,38 +499,6 @@ "markers": "python_version >= '3.5'", "version": "==5.1.1" }, - "defusedxml": { - "hashes": [ - "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", - "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.7.1" - }, - "descartes": { - "hashes": [ - "sha256:135a502146af5ed6ff359975e2ebc5fa4b71b5432c355c2cafdc6dea1337035b", - "sha256:4c62dc41109689d03e4b35de0a2bcbdeeb81047badc607c4415d5c753bd683af", - "sha256:b7e412e7e6e294412f1d0f661f187babc970088c2456089e6801eebb043c2e1b" - ], - "version": "==1.1.0" - }, - "distributed": { - "hashes": [ - "sha256:73ce33bd2460bd45ffc793ffdf9066bd2a3b6bbc65079f74f5147eafcda9b1cb", - "sha256:74e3f7f68d4dc435a3591ae1ad8ce7d5a11211fd22692e39c7e50aa11bf7e385" - ], - "markers": "python_version >= '3.8'", - "version": "==2023.5.0" - }, - "docker": { - "hashes": [ - "sha256:aa6d17830045ba5ef0168d5eaa34d37beeb113948c413affe1d5991fc11f9a20", - "sha256:aecd2277b8bf8e506e484f6ab7aec39abe0038e29fa4a6d3ba86c3fe01844ed9" - ], - "markers": "python_version >= '3.7'", - "version": "==6.1.3" - }, "empty-files": { "hashes": [ "sha256:22e74de327271ef9592a661aaa53bb1d5fa6cfd539dac7315625c8b30b7292ae", @@ -689,14 +507,6 @@ "markers": "python_full_version >= '3.7.1'", "version": "==0.0.9" }, - "entrypoints": { - "hashes": [ - "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4", - "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f" - ], - "markers": "python_version >= '3.6'", - "version": "==0.4" - }, "exceptiongroup": { "hashes": [ "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9", @@ -713,49 +523,12 @@ "markers": "python_version >= '3.5'", "version": "==2.0.1" }, - "fastjsonschema": { - "hashes": [ - "sha256:06dc8680d937628e993fa0cd278f196d20449a1adc087640710846b324d422ea", - "sha256:aec6a19e9f66e9810ab371cc913ad5f4e9e479b63a7072a2cd060a9369e329a8" - ], - "version": "==2.18.1" - }, "finist": { "hashes": [ "sha256:795fc4d9f73e6e0e1af4c151d577f4a7b31bd591cd7163e1a99bb4a17faf2742" ], "version": "==0.1.2" }, - "fiona": { - "hashes": [ - "sha256:35dae4b0308eb44617cdc4461ceb91f891d944fdebbcba5479efe524ec5db8de", - "sha256:374efe749143ecb5cfdd79b585d83917d2bf8ecfbfc6953c819586b336ce9c63", - "sha256:3ea04ec2d8c57b5f81a31200fb352cb3242aa106fc3e328963f30ffbdf0ff7c8", - "sha256:4877cc745d9e82b12b3eafce3719db75759c27bd8a695521202135b36b58c2e7", - "sha256:4df21906235928faad856c288cfea0298e9647f09c9a69a230535cbc8eadfa21", - "sha256:5b4c6a3df53bee8f85bb46685562b21b43346be1fe96419f18f70fa1ab8c561c", - "sha256:5f40a40529ecfca5294260316cf987a0420c77a2f0cf0849f529d1afbccd093e", - "sha256:6ad04c1877b9fd742871b11965606c6a52f40706f56a48d66a87cc3073943828", - "sha256:6d9f29e9bcbb33232ff7fa98b4a3c2234db910c1dc6c4147fc36c0b8b930f2e0", - "sha256:7ede8ddc798f3d447536080c6db9a5fb73733ad8bdb190cb65eed4e289dd4c50", - "sha256:81d502369493687746cb8d3cd77e5ada4447fb71d513721c9a1826e4fb32b23a", - "sha256:8b098054a27c12afac4f819f98cb4d4bf2db9853f70b0c588d7d97d26e128c39", - "sha256:8b53ce8de773fcd5e2e102e833c8c58479edd8796a522f3d83ef9e08b62bfeea", - "sha256:99e2604332caa7692855c2ae6ed91e1fffdf9b59449aa8032dd18e070e59a2f7", - "sha256:9a2da52f865db1aff0eaf41cdd4c87a7c079b3996514e8e7a1ca38457309e825", - "sha256:9fb9a24a8046c724787719e20557141b33049466145fc3e665764ac7caf5748c", - "sha256:ac2c250f509ec19fad7959d75b531984776517ef3c1222d1cc5b4f962825880b", - "sha256:bd2355e859a1cd24a3e485c6dc5003129f27a2051629def70036535ffa7e16a4", - "sha256:c521e1135c78dec0d7774303e5a1b4c62e0efb0e602bb8f167550ef95e0a2691", - "sha256:ce3b29230ef70947ead4e701f3f82be81082b7f37fd4899009b1445cc8fc276a", - "sha256:cfef6db5b779d463298b1113b50daa6c5b55f26f834dc9e37752116fa17277c1", - "sha256:d722d7f01a66f4ab6cd08d156df3fdb92f0669cf5f8708ddcb209352f416f241", - "sha256:f1af08da4ecea5036cb81c9131946be4404245d1b434b5b24fd3871a1d4030d9", - "sha256:fce4b1dd98810cabccdaa1828430c7402d283295c2ae31bea4f34188ea9e88d7" - ], - "markers": "python_version >= '3.7'", - "version": "==1.9.5" - }, "flask": { "hashes": [ "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", @@ -783,78 +556,14 @@ "editable": true, "path": "./../flowmachine" }, - "fonttools": { - "hashes": [ - "sha256:10003ebd81fec0192c889e63a9c8c63f88c7d72ae0460b7ba0cd2a1db246e5ad", - "sha256:10b3922875ffcba636674f406f9ab9a559564fdbaa253d66222019d569db869c", - "sha256:13a9a185259ed144def3682f74fdcf6596f2294e56fe62dfd2be736674500dba", - "sha256:17dbc2eeafb38d5d0e865dcce16e313c58265a6d2d20081c435f84dc5a9d8212", - "sha256:18a2477c62a728f4d6e88c45ee9ee0229405e7267d7d79ce1f5ce0f3e9f8ab86", - "sha256:18eefac1b247049a3a44bcd6e8c8fd8b97f3cad6f728173b5d81dced12d6c477", - "sha256:1952c89a45caceedf2ab2506d9a95756e12b235c7182a7a0fff4f5e52227204f", - "sha256:1cf9e974f63b1080b1d2686180fc1fbfd3bfcfa3e1128695b5de337eb9075cef", - "sha256:1e09da7e8519e336239fbd375156488a4c4945f11c4c5792ee086dd84f784d02", - "sha256:2062542a7565091cea4cc14dd99feff473268b5b8afdee564f7067dd9fff5860", - "sha256:25d3da8a01442cbc1106490eddb6d31d7dffb38c1edbfabbcc8db371b3386d72", - "sha256:34f713dad41aa21c637b4e04fe507c36b986a40f7179dcc86402237e2d39dcd3", - "sha256:360201d46165fc0753229afe785900bc9596ee6974833124f4e5e9f98d0f592b", - "sha256:3b7ad05b2beeebafb86aa01982e9768d61c2232f16470f9d0d8e385798e37184", - "sha256:4c54466f642d2116686268c3e5f35ebb10e49b0d48d41a847f0e171c785f7ac7", - "sha256:4d9740e3783c748521e77d3c397dc0662062c88fd93600a3c2087d3d627cd5e5", - "sha256:4f88cae635bfe4bbbdc29d479a297bb525a94889184bb69fa9560c2d4834ddb9", - "sha256:51669b60ee2a4ad6c7fc17539a43ffffc8ef69fd5dbed186a38a79c0ac1f5db7", - "sha256:5db46659cfe4e321158de74c6f71617e65dc92e54980086823a207f1c1c0e24b", - "sha256:5f37e31291bf99a63328668bb83b0669f2688f329c4c0d80643acee6e63cd933", - "sha256:6bb5ea9076e0e39defa2c325fc086593ae582088e91c0746bee7a5a197be3da0", - "sha256:748015d6f28f704e7d95cd3c808b483c5fb87fd3eefe172a9da54746ad56bfb6", - "sha256:7bbbf8174501285049e64d174e29f9578495e1b3b16c07c31910d55ad57683d8", - "sha256:884ef38a5a2fd47b0c1291647b15f4e88b9de5338ffa24ee52c77d52b4dfd09c", - "sha256:8da417431bfc9885a505e86ba706f03f598c85f5a9c54f67d63e84b9948ce590", - "sha256:95e974d70238fc2be5f444fa91f6347191d0e914d5d8ae002c9aa189572cc215", - "sha256:9648518ef687ba818db3fcc5d9aae27a369253ac09a81ed25c3867e8657a0680", - "sha256:9a2f0aa6ca7c9bc1058a9d0b35483d4216e0c1bbe3962bc62ce112749954c7b8", - "sha256:9c36da88422e0270fbc7fd959dc9749d31a958506c1d000e16703c2fce43e3d0", - "sha256:9c60ecfa62839f7184f741d0509b5c039d391c3aff71dc5bc57b87cc305cff3b", - "sha256:9f727c3e3d08fd25352ed76cc3cb61486f8ed3f46109edf39e5a60fc9fecf6ca", - "sha256:a7a06f8d95b7496e53af80d974d63516ffb263a468e614978f3899a6df52d4b3", - "sha256:ad0b3f6342cfa14be996971ea2b28b125ad681c6277c4cd0fbdb50340220dfb6", - "sha256:b2adca1b46d69dce4a37eecc096fe01a65d81a2f5c13b25ad54d5430ae430b13", - "sha256:b84a1c00f832feb9d0585ca8432fba104c819e42ff685fcce83537e2e7e91204", - "sha256:bb6d2f8ef81ea076877d76acfb6f9534a9c5f31dc94ba70ad001267ac3a8e56f", - "sha256:bf11e2cca121df35e295bd34b309046c29476ee739753bc6bc9d5050de319273", - "sha256:d21099b411e2006d3c3e1f9aaf339e12037dbf7bf9337faf0e93ec915991f43b", - "sha256:d4071bd1c183b8d0b368cc9ed3c07a0f6eb1bdfc4941c4c024c49a35429ac7cd", - "sha256:e117a92b07407a061cde48158c03587ab97e74e7d73cb65e6aadb17af191162a", - "sha256:f7a58eb5e736d7cf198eee94844b81c9573102ae5989ebcaa1d1a37acd04b33d", - "sha256:fe9b1ec799b6086460a7480e0f55c447b1aca0a4eecc53e444f639e967348896" - ], - "markers": "python_version >= '3.8'", - "version": "==4.43.1" - }, - "fsspec": { - "hashes": [ - "sha256:330c66757591df346ad3091a53bd907e15348c2ba17d63fd54f5c39c4457d2a5", - "sha256:346a8f024efeb749d2a5fca7ba8854474b1ff9af7c3faaf636a4548781136529" - ], - "markers": "python_version >= '3.8'", - "version": "==2023.10.0" - }, "geojson": { "hashes": [ - "sha256:e49df982b204ed481e4c1236c57f587adf71537301cf8faf7120ab27d73c7568", - "sha256:ff3d75acab60b1e66504a11f7ea12c104bad32ff3c410a807788663b966dee4a" + "sha256:58a7fa40727ea058efc28b0e9ff0099eadf6d0965e04690830208d3ef571adac", + "sha256:68a9771827237adb8c0c71f8527509c8f5bef61733aa434cefc9c9d4f0ebe8f3" ], "index": "pypi", - "markers": "python_version < '3.12' and python_version >= '3.7'", - "version": "==3.0.1" - }, - "geopandas": { - "hashes": [ - "sha256:101cfd0de54bcf9e287a55b5ea17ebe0db53a5e25a28bacf100143d0507cabd9", - "sha256:e5b56d9c20800c77bcc0c914db3f27447a37b23b2cd892be543f5001a694a968" - ], - "markers": "python_version >= '3.8'", - "version": "==0.13.2" + "markers": "python_version >= '3.7'", + "version": "==3.1.0" }, "get-secret-or-env-var": { "hashes": [ @@ -923,7 +632,7 @@ "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064", "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36" ], - "markers": "python_version >= '3.7'", + "markers": "python_version >= '3' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", "version": "==3.0.1" }, "h11": { @@ -952,19 +661,19 @@ }, "httpcore": { "hashes": [ - "sha256:13b5e5cd1dca1a6636a6aaea212b19f4f85cd88c366a2b82304181b769aab3c9", - "sha256:adc5398ee0a476567bf87467063ee63584a8bce86078bf748e48754f60202ced" + "sha256:c5e97ef177dca2023d0b9aad98e49507ef5423e9f1d94ffe2cfe250aa28e63b0", + "sha256:fce1ddf9b606cfb98132ab58865c3728c52c8e4c3c46e2aabb3674464a186e92" ], "markers": "python_version >= '3.8'", - "version": "==0.18.0" + "version": "==1.0.1" }, "httpx": { "hashes": [ - "sha256:181ea7f8ba3a82578be86ef4171554dd45fec26a02556a744db029a0a27b7100", - "sha256:47ecda285389cb32bb2691cc6e069e3ab0205956f681c5b2ad2325719751d875" + "sha256:fec7d6cc5c27c578a391f7e87b9aa7d3d8fbcd034f6399f9f79b45bcc12a866a", + "sha256:ffd96d5cf901e63863d9f1b4b6807861dbea4d301613415d9e6e57ead15fc5d0" ], "markers": "python_version >= '3.8'", - "version": "==0.25.0" + "version": "==0.25.1" }, "hypercorn": { "hashes": [ @@ -1000,11 +709,11 @@ }, "importlib-resources": { "hashes": [ - "sha256:9d48dcccc213325e810fd723e7fbb45ccb39f6cf5c31f00cf2b965f5f10f3cb9", - "sha256:aa50258bbfa56d4e33fbd8aa3ef48ded10d1735f11532b8df95388cc6bdb7e83" + "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a", + "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6" ], "markers": "python_version < '3.9'", - "version": "==6.1.0" + "version": "==6.1.1" }, "iniconfig": { "hashes": [ @@ -1014,14 +723,6 @@ "markers": "python_version >= '3.7'", "version": "==2.0.0" }, - "ipykernel": { - "hashes": [ - "sha256:3ba3dc97424b87b31bb46586b5167b3161b32d7820b9201a9e698c71e271602c", - "sha256:553856658eb8430bbe9653ea041a41bff63e9606fc4628873fc92a6cf3abd404" - ], - "markers": "python_version >= '3.8'", - "version": "==6.26.0" - }, "ipython": { "hashes": [ "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363", @@ -1069,30 +770,6 @@ "markers": "python_version >= '3.7'", "version": "==4.17.3" }, - "jupyter-client": { - "hashes": [ - "sha256:c3877aac7257ec68d79b5c622ce986bd2a992ca42f6ddc9b4dd1da50e89f7028", - "sha256:e8754066510ce456358df363f97eae64b50860f30dc1fe8c6771440db3be9a63" - ], - "markers": "python_version >= '3.8'", - "version": "==8.5.0" - }, - "jupyter-core": { - "hashes": [ - "sha256:880b86053bf298a8724994f95e99b99130659022a4f7f45f563084b6223861d3", - "sha256:e11e02cd8ae0a9de5c6c44abf5727df9f2581055afe00b22183f621ba3585805" - ], - "markers": "python_version >= '3.8'", - "version": "==5.5.0" - }, - "jupyterlab-pygments": { - "hashes": [ - "sha256:2405800db07c9f770863bcf8049a529c3dd4d3e28536638bd7c1c01d2748309f", - "sha256:7405d7fde60819d905a9fa8ce89e4cd830e318cdad22a0030f7a901da705585d" - ], - "markers": "python_version >= '3.7'", - "version": "==0.2.2" - }, "jupyterlab-widgets": { "hashes": [ "sha256:3cf5bdf5b897bf3bccf1c11873aa4afd776d7430200f765e0686bd352487b58d", @@ -1101,124 +778,6 @@ "markers": "python_version >= '3.7'", "version": "==3.0.9" }, - "kiwisolver": { - "hashes": [ - "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf", - "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e", - "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af", - "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f", - "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046", - "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3", - "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5", - "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71", - "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee", - "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3", - "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9", - "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b", - "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985", - "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea", - "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16", - "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89", - "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c", - "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9", - "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712", - "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342", - "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a", - "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958", - "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d", - "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a", - "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130", - "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff", - "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898", - "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b", - "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f", - "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265", - "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93", - "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929", - "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635", - "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709", - "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b", - "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb", - "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a", - "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920", - "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e", - "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544", - "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45", - "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390", - "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77", - "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355", - "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff", - "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4", - "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7", - "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20", - "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c", - "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162", - "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228", - "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437", - "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc", - "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a", - "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901", - "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4", - "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770", - "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525", - "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad", - "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a", - "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29", - "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90", - "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250", - "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d", - "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3", - "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54", - "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f", - "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1", - "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da", - "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238", - "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa", - "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523", - "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0", - "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205", - "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3", - "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4", - "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac", - "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9", - "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb", - "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced", - "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd", - "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0", - "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da", - "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18", - "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9", - "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276", - "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333", - "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b", - "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db", - "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126", - "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9", - "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09", - "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0", - "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec", - "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7", - "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff", - "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9", - "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192", - "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8", - "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d", - "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6", - "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797", - "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892", - "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f" - ], - "markers": "python_version >= '3.7'", - "version": "==1.4.5" - }, - "locket": { - "hashes": [ - "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632", - "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.0.0" - }, "markupsafe": { "hashes": [ "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", @@ -1301,59 +860,6 @@ "markers": "python_version >= '3.6'", "version": "==3.0.1" }, - "matplotlib": { - "hashes": [ - "sha256:085c33b27561d9c04386789d5aa5eb4a932ddef43cfcdd0e01735f9a6e85ce0c", - "sha256:0a97af9d22e8ebedc9f00b043d9bbd29a375e9e10b656982012dded44c10fd77", - "sha256:122dcbf9be0086e2a95d9e5e0632dbf3bd5b65eaa68c369363310a6c87753059", - "sha256:12b4f6795efea037ce2d41e7c417ad8bd02d5719c6ad4a8450a0708f4a1cfb89", - "sha256:12f4c0dd8aa280d796c8772ea8265a14f11a04319baa3a16daa5556065e8baea", - "sha256:165c8082bf8fc0360c24aa4724a22eaadbfd8c28bf1ccf7e94d685cad48261e4", - "sha256:1990955b11e7918d256cf3b956b10997f405b7917a3f1c7d8e69c1d15c7b1930", - "sha256:1a6094c6f8e8d18db631754df4fe9a34dec3caf074f6869a7db09f18f9b1d6b2", - "sha256:1f9c6c16597af660433ab330b59ee2934b832ee1fabcaf5cbde7b2add840f31e", - "sha256:22d65d18b4ee8070a5fea5761d59293f1f9e2fac37ec9ce090463b0e629432fd", - "sha256:236024f582e40dac39bca592258888b38ae47a9fed7b8de652d68d3d02d47d2b", - "sha256:259999c05285cb993d7f2a419cea547863fa215379eda81f7254c9e932963729", - "sha256:272dba2f1b107790ed78ebf5385b8d14b27ad9e90419de340364b49fe549a993", - "sha256:336e88900c11441e458da01c8414fc57e04e17f9d3bb94958a76faa2652bcf6b", - "sha256:39018a2b17592448fbfdf4b8352955e6c3905359939791d4ff429296494d1a0c", - "sha256:3bf3a178c6504694cee8b88b353df0051583f2f6f8faa146f67115c27c856881", - "sha256:3f4e7fd5a6157e1d018ce2166ec8e531a481dd4a36f035b5c23edfe05a25419a", - "sha256:40e3b9b450c6534f07278310c4e34caff41c2a42377e4b9d47b0f8d3ac1083a2", - "sha256:498a08267dc69dd8f24c4b5d7423fa584d7ce0027ba71f7881df05fc09b89bb7", - "sha256:4aab27d9e33293389e3c1d7c881d414a72bdfda0fedc3a6bf46c6fa88d9b8015", - "sha256:55de4cf7cd0071b8ebf203981b53ab64f988a0a1f897a2dff300a1124e8bcd8b", - "sha256:591c123bed1cb4b9996fb60b41a6d89c2ec4943244540776c5f1283fb6960a53", - "sha256:67a410a9c9e07cbc83581eeea144bbe298870bf0ac0ee2f2e10a015ab7efee19", - "sha256:6eaa1cf0e94c936a26b78f6d756c5fbc12e0a58c8a68b7248a2a31456ce4e234", - "sha256:7153453669c9672b52095119fd21dd032d19225d48413a2871519b17db4b0fde", - "sha256:747c6191d2e88ae854809e69aa358dbf852ff1a5738401b85c1cc9012309897a", - "sha256:74bf57f505efea376097e948b7cdd87191a7ce8180616390aef496639edf601f", - "sha256:755bafc10a46918ce9a39980009b54b02dd249594e5adf52f9c56acfddb5d0b7", - "sha256:78b2136cc6c5415b78977e0e8c608647d597204b05b1d9089ccf513c7d913733", - "sha256:7baf98c5ad59c5c4743ea884bb025cbffa52dacdfdac0da3e6021a285a90377e", - "sha256:91e36a85ea639a1ba9f91427041eac064b04829945fe331a92617b6cb21d27e5", - "sha256:9c40cde976c36693cc0767e27cf5f443f91c23520060bd9496678364adfafe9c", - "sha256:a7240259b4b9cbc62381f6378cff4d57af539162a18e832c1e48042fabc40b6b", - "sha256:ac03377fd908aaee2312d0b11735753e907adb6f4d1d102de5e2425249693f6c", - "sha256:c568e80e1c17f68a727f30f591926751b97b98314d8e59804f54f86ae6fa6a22", - "sha256:caf5eaaf7c68f8d7df269dfbcaf46f48a70ff482bfcebdcc97519671023f2a7d", - "sha256:d48999c4b19b5a0c058c9cd828ff6fc7748390679f6cf9a2ad653a3e802c87d3", - "sha256:d5adc743de91e8e0b13df60deb1b1c285b8effea3d66223afceb14b63c9b05de", - "sha256:dfc118642903a23e309b1da32886bb39a4314147d013e820c86b5fb4cb2e36d0", - "sha256:e594ee43c59ea39ca5c6244667cac9d017a3527febc31f5532ad9135cf7469ec", - "sha256:e78707b751260b42b721507ad7aa60fe4026d7f51c74cca6b9cd8b123ebb633a", - "sha256:ebd8470cc2a3594746ff0513aecbfa2c55ff6f58e6cef2efb1a54eb87c88ffa2", - "sha256:ec726b08a5275d827aa91bb951e68234a4423adb91cf65bc0fcdc0f2777663f7", - "sha256:edf54cac8ee3603f3093616b40a931e8c063969756a4d78a86e82c2fea9659f7", - "sha256:ee152a88a0da527840a426535514b6ed8ac4240eb856b1da92cf48124320e346", - "sha256:f09b3dd6bdeb588de91f853bbb2d6f0ff8ab693485b0c49035eaa510cb4f142e", - "sha256:faa3d12d8811d08d14080a8b7b9caea9a457dc495350166b56df0db4b9909ef5" - ], - "markers": "python_version >= '3.8'", - "version": "==3.7.3" - }, "matplotlib-inline": { "hashes": [ "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311", @@ -1369,14 +875,6 @@ ], "version": "==0.1.5" }, - "mistune": { - "hashes": [ - "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205", - "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8" - ], - "markers": "python_version >= '3.7'", - "version": "==3.0.2" - }, "mock": { "hashes": [ "sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744", @@ -1392,68 +890,6 @@ ], "version": "==0.7.4" }, - "msgpack": { - "hashes": [ - "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862", - "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d", - "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3", - "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672", - "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0", - "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9", - "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee", - "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46", - "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524", - "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819", - "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc", - "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc", - "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1", - "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82", - "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81", - "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6", - "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d", - "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2", - "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c", - "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87", - "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84", - "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e", - "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95", - "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f", - "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b", - "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93", - "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf", - "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61", - "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c", - "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8", - "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d", - "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c", - "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4", - "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba", - "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415", - "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee", - "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d", - "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9", - "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075", - "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f", - "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7", - "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681", - "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329", - "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1", - "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf", - "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c", - "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5", - "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b", - "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5", - "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e", - "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b", - "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad", - "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd", - "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7", - "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002", - "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc" - ], - "markers": "python_version >= '3.8'", - "version": "==1.0.7" - }, "mypy-extensions": { "hashes": [ "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", @@ -1462,38 +898,6 @@ "markers": "python_version >= '3.5'", "version": "==1.0.0" }, - "nbclient": { - "hashes": [ - "sha256:25e861299e5303a0477568557c4045eccc7a34c17fc08e7959558707b9ebe548", - "sha256:f9b179cd4b2d7bca965f900a2ebf0db4a12ebff2f36a711cb66861e4ae158e55" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==0.8.0" - }, - "nbconvert": { - "hashes": [ - "sha256:4bedff08848626be544de193b7594d98a048073f392178008ff4f171f5e21d26", - "sha256:8cf1d95e569730f136feb85e4bba25bdcf3a63fefb122d854ddff6771c0ac933" - ], - "markers": "python_version >= '3.8'", - "version": "==7.10.0" - }, - "nbformat": { - "hashes": [ - "sha256:1c5172d786a41b82bcfd0c23f9e6b6f072e8fb49c39250219e4acfff1efe89e9", - "sha256:5f98b5ba1997dff175e77e0c17d5c10a96eaed2cbd1de3533d1fc35d5e111192" - ], - "markers": "python_version >= '3.8'", - "version": "==5.9.2" - }, - "nest-asyncio": { - "hashes": [ - "sha256:25aa2ca0d2a5b5531956b9e273b45cf664cae2b145101d73b86b199978d48fdb", - "sha256:accda7a339a70599cb08f9dd09a67e0c2ef8d8d6f4c07f96ab203f2ae254e48d" - ], - "markers": "python_version >= '3.5'", - "version": "==1.5.8" - }, "networkx": { "hashes": [ "sha256:4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36", @@ -1502,14 +906,6 @@ "markers": "python_version >= '3.8'", "version": "==3.1" }, - "nteract-scrapbook": { - "hashes": [ - "sha256:10ec7a33c32ceae5fdd7fb84260c5184c36c7f2b4f6f7c4679b9761f766e7e35", - "sha256:1a5e2d9d04b2d2ce2d05e430429966411ee765c263e4f9aecfa01807fc3b23b2" - ], - "markers": "python_version >= '3.5'", - "version": "==0.4.2" - }, "numpy": { "hashes": [ "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f", @@ -1598,22 +994,6 @@ "markers": "python_version >= '3.8'", "version": "==2.0.3" }, - "pandocfilters": { - "hashes": [ - "sha256:0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38", - "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.5.0" - }, - "papermill": { - "hashes": [ - "sha256:6f8f8a9b06b39677f207c09100c8d386bcf592f0cbbdda9f0f50e81445697627", - "sha256:baa76f0441257d9a25b3ad7c895e761341b94f9a70ca98cf419247fc728932d9" - ], - "markers": "python_version >= '3.7'", - "version": "==2.4.0" - }, "parso": { "hashes": [ "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0", @@ -1622,14 +1002,6 @@ "markers": "python_version >= '3.6'", "version": "==0.8.3" }, - "partd": { - "hashes": [ - "sha256:27e766663d36c161e2827aa3e28541c992f0b9527d3cca047e13fb3acdb989e6", - "sha256:56c25dd49e6fea5727e731203c466c6e092f308d8f0024e199d02f6aa2167f67" - ], - "markers": "python_version >= '3.7'", - "version": "==1.4.1" - }, "pathspec": { "hashes": [ "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20", @@ -1638,33 +1010,6 @@ "markers": "python_version >= '3.7'", "version": "==0.11.2" }, - "pendulum": { - "hashes": [ - "sha256:0731f0c661a3cb779d398803655494893c9f581f6488048b3fb629c2342b5394", - "sha256:1245cd0075a3c6d889f581f6325dd8404aca5884dea7223a5566c38aab94642b", - "sha256:29c40a6f2942376185728c9a0347d7c0f07905638c83007e1d262781f1e6953a", - "sha256:2d1619a721df661e506eff8db8614016f0720ac171fe80dda1333ee44e684087", - "sha256:318f72f62e8e23cd6660dbafe1e346950281a9aed144b5c596b2ddabc1d19739", - "sha256:33fb61601083f3eb1d15edeb45274f73c63b3c44a8524703dc143f4212bf3269", - "sha256:3481fad1dc3f6f6738bd575a951d3c15d4b4ce7c82dce37cf8ac1483fde6e8b0", - "sha256:4c9c689747f39d0d02a9f94fcee737b34a5773803a64a5fdb046ee9cac7442c5", - "sha256:7c5ec650cb4bec4c63a89a0242cc8c3cebcec92fcfe937c417ba18277d8560be", - "sha256:94b1fc947bfe38579b28e1cccb36f7e28a15e841f30384b5ad6c5e31055c85d7", - "sha256:9702069c694306297ed362ce7e3c1ef8404ac8ede39f9b28b7c1a7ad8c3959e3", - "sha256:b06a0ca1bfe41c990bbf0c029f0b6501a7f2ec4e38bfec730712015e8860f207", - "sha256:b6c352f4bd32dff1ea7066bd31ad0f71f8d8100b9ff709fb343f3b86cee43efe", - "sha256:c501749fdd3d6f9e726086bf0cd4437281ed47e7bca132ddb522f86a1645d360", - "sha256:c807a578a532eeb226150d5006f156632df2cc8c5693d778324b43ff8c515dd0", - "sha256:db0a40d8bcd27b4fb46676e8eb3c732c67a5a5e6bfab8927028224fbced0b40b", - "sha256:de42ea3e2943171a9e95141f2eecf972480636e8e484ccffaf1e833929e9e052", - "sha256:e95d329384717c7bf627bf27e204bc3b15c8238fa8d9d9781d93712776c14002", - "sha256:f5e236e7730cab1644e1b87aca3d2ff3e375a608542e90fe25685dae46310116", - "sha256:f888f2d2909a414680a29ae74d0592758f2b9fcdee3549887779cd4055e975db", - "sha256:fb53ffa0085002ddd43b6ca61a7b34f2d4d7c3ed66f931fe599e1a531b42af9b" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==2.1.2" - }, "pexpect": { "hashes": [ "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937", @@ -1721,66 +1066,6 @@ ], "version": "==0.7.5" }, - "pillow": { - "hashes": [ - "sha256:00f438bb841382b15d7deb9a05cc946ee0f2c352653c7aa659e75e592f6fa17d", - "sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de", - "sha256:04f6f6149f266a100374ca3cc368b67fb27c4af9f1cc8cb6306d849dcdf12616", - "sha256:062a1610e3bc258bff2328ec43f34244fcec972ee0717200cb1425214fe5b839", - "sha256:0a026c188be3b443916179f5d04548092e253beb0c3e2ee0a4e2cdad72f66099", - "sha256:0f7c276c05a9767e877a0b4c5050c8bee6a6d960d7f0c11ebda6b99746068c2a", - "sha256:1a8413794b4ad9719346cd9306118450b7b00d9a15846451549314a58ac42219", - "sha256:1ab05f3db77e98f93964697c8efc49c7954b08dd61cff526b7f2531a22410106", - "sha256:1c3ac5423c8c1da5928aa12c6e258921956757d976405e9467c5f39d1d577a4b", - "sha256:1c41d960babf951e01a49c9746f92c5a7e0d939d1652d7ba30f6b3090f27e412", - "sha256:1fafabe50a6977ac70dfe829b2d5735fd54e190ab55259ec8aea4aaea412fa0b", - "sha256:1fb29c07478e6c06a46b867e43b0bcdb241b44cc52be9bc25ce5944eed4648e7", - "sha256:24fadc71218ad2b8ffe437b54876c9382b4a29e030a05a9879f615091f42ffc2", - "sha256:2cdc65a46e74514ce742c2013cd4a2d12e8553e3a2563c64879f7c7e4d28bce7", - "sha256:2ef6721c97894a7aa77723740a09547197533146fba8355e86d6d9a4a1056b14", - "sha256:3b834f4b16173e5b92ab6566f0473bfb09f939ba14b23b8da1f54fa63e4b623f", - "sha256:3d929a19f5469b3f4df33a3df2983db070ebb2088a1e145e18facbc28cae5b27", - "sha256:41f67248d92a5e0a2076d3517d8d4b1e41a97e2df10eb8f93106c89107f38b57", - "sha256:47e5bf85b80abc03be7455c95b6d6e4896a62f6541c1f2ce77a7d2bb832af262", - "sha256:4d0152565c6aa6ebbfb1e5d8624140a440f2b99bf7afaafbdbf6430426497f28", - "sha256:50d08cd0a2ecd2a8657bd3d82c71efd5a58edb04d9308185d66c3a5a5bed9610", - "sha256:61f1a9d247317fa08a308daaa8ee7b3f760ab1809ca2da14ecc88ae4257d6172", - "sha256:6932a7652464746fcb484f7fc3618e6503d2066d853f68a4bd97193a3996e273", - "sha256:7a7e3daa202beb61821c06d2517428e8e7c1aab08943e92ec9e5755c2fc9ba5e", - "sha256:7dbaa3c7de82ef37e7708521be41db5565004258ca76945ad74a8e998c30af8d", - "sha256:7df5608bc38bd37ef585ae9c38c9cd46d7c81498f086915b0f97255ea60c2818", - "sha256:806abdd8249ba3953c33742506fe414880bad78ac25cc9a9b1c6ae97bedd573f", - "sha256:883f216eac8712b83a63f41b76ddfb7b2afab1b74abbb413c5df6680f071a6b9", - "sha256:912e3812a1dbbc834da2b32299b124b5ddcb664ed354916fd1ed6f193f0e2d01", - "sha256:937bdc5a7f5343d1c97dc98149a0be7eb9704e937fe3dc7140e229ae4fc572a7", - "sha256:9882a7451c680c12f232a422730f986a1fcd808da0fd428f08b671237237d651", - "sha256:9a92109192b360634a4489c0c756364c0c3a2992906752165ecb50544c251312", - "sha256:9d7bc666bd8c5a4225e7ac71f2f9d12466ec555e89092728ea0f5c0c2422ea80", - "sha256:a5f63b5a68daedc54c7c3464508d8c12075e56dcfbd42f8c1bf40169061ae666", - "sha256:a646e48de237d860c36e0db37ecaecaa3619e6f3e9d5319e527ccbc8151df061", - "sha256:a89b8312d51715b510a4fe9fc13686283f376cfd5abca8cd1c65e4c76e21081b", - "sha256:a92386125e9ee90381c3369f57a2a50fa9e6aa8b1cf1d9c4b200d41a7dd8e992", - "sha256:ae88931f93214777c7a3aa0a8f92a683f83ecde27f65a45f95f22d289a69e593", - "sha256:afc8eef765d948543a4775f00b7b8c079b3321d6b675dde0d02afa2ee23000b4", - "sha256:b0eb01ca85b2361b09480784a7931fc648ed8b7836f01fb9241141b968feb1db", - "sha256:b1c25762197144e211efb5f4e8ad656f36c8d214d390585d1d21281f46d556ba", - "sha256:b4005fee46ed9be0b8fb42be0c20e79411533d1fd58edabebc0dd24626882cfd", - "sha256:b920e4d028f6442bea9a75b7491c063f0b9a3972520731ed26c83e254302eb1e", - "sha256:baada14941c83079bf84c037e2d8b7506ce201e92e3d2fa0d1303507a8538212", - "sha256:bb40c011447712d2e19cc261c82655f75f32cb724788df315ed992a4d65696bb", - "sha256:c0949b55eb607898e28eaccb525ab104b2d86542a85c74baf3a6dc24002edec2", - "sha256:c9aeea7b63edb7884b031a35305629a7593272b54f429a9869a4f63a1bf04c34", - "sha256:cfe96560c6ce2f4c07d6647af2d0f3c54cc33289894ebd88cfbb3bcd5391e256", - "sha256:d27b5997bdd2eb9fb199982bb7eb6164db0426904020dc38c10203187ae2ff2f", - "sha256:d921bc90b1defa55c9917ca6b6b71430e4286fc9e44c55ead78ca1a9f9eba5f2", - "sha256:e6bf8de6c36ed96c86ea3b6e1d5273c53f46ef518a062464cd7ef5dd2cf92e38", - "sha256:eaed6977fa73408b7b8a24e8b14e59e1668cfc0f4c40193ea7ced8e210adf996", - "sha256:fa1d323703cfdac2036af05191b969b910d8f115cf53093125e4058f62012c9a", - "sha256:fe1e26e1ffc38be097f0ba1d0d07fcade2bcfd1d023cda5b29935ae8052bd793" - ], - "markers": "python_version >= '3.8'", - "version": "==10.1.0" - }, "pkgutil-resolve-name": { "hashes": [ "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174", @@ -1816,14 +1101,6 @@ "markers": "python_version >= '3.6'", "version": "==0.21.8.0" }, - "prefect": { - "hashes": [ - "sha256:179f179849286bb8dc0309c8718a7815e6e5fcc016398d2aea45e16fa0e3471b", - "sha256:a838d427a88845b13279b89b925e2b6acde5ff2bb090c5480617bc6047a808a8" - ], - "markers": "python_version >= '3.7'", - "version": "==1.4.1" - }, "priority": { "hashes": [ "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", @@ -1840,28 +1117,6 @@ "markers": "python_full_version >= '3.7.0'", "version": "==3.0.39" }, - "psutil": { - "hashes": [ - "sha256:10e8c17b4f898d64b121149afb136c53ea8b68c7531155147867b7b1ac9e7e28", - "sha256:18cd22c5db486f33998f37e2bb054cc62fd06646995285e02a51b1e08da97017", - "sha256:3ebf2158c16cc69db777e3c7decb3c0f43a7af94a60d72e87b2823aebac3d602", - "sha256:51dc3d54607c73148f63732c727856f5febec1c7c336f8f41fcbd6315cce76ac", - "sha256:6e5fb8dc711a514da83098bc5234264e551ad980cec5f85dabf4d38ed6f15e9a", - "sha256:70cb3beb98bc3fd5ac9ac617a327af7e7f826373ee64c80efd4eb2856e5051e9", - "sha256:748c9dd2583ed86347ed65d0035f45fa8c851e8d90354c122ab72319b5f366f4", - "sha256:91ecd2d9c00db9817a4b4192107cf6954addb5d9d67a969a4f436dbc9200f88c", - "sha256:92e0cc43c524834af53e9d3369245e6cc3b130e78e26100d1f63cdb0abeb3d3c", - "sha256:a6f01f03bf1843280f4ad16f4bde26b817847b4c1a0db59bf6419807bc5ce05c", - "sha256:c69596f9fc2f8acd574a12d5f8b7b1ba3765a641ea5d60fb4736bf3c08a8214a", - "sha256:ca2780f5e038379e520281e4c032dddd086906ddff9ef0d1b9dcf00710e5071c", - "sha256:daecbcbd29b289aac14ece28eca6a3e60aa361754cf6da3dfb20d4d32b6c7f57", - "sha256:e4b92ddcd7dd4cdd3f900180ea1e104932c7bce234fb88976e2a3b296441225a", - "sha256:fb8a697f11b0f5994550555fcfe3e69799e5b060c8ecf9e2f75c69302cc35c0d", - "sha256:ff18b8d1a784b810df0b0fff3bcb50ab941c3b8e2c8de5726f9c71c601c611aa" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==5.9.6" - }, "psycopg2-binary": { "hashes": [ "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9", @@ -1914,6 +1169,7 @@ "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041", "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7", "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860", + "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d", "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245", "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27", "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417", @@ -1953,41 +1209,6 @@ ], "version": "==0.2.2" }, - "pyarrow": { - "hashes": [ - "sha256:09552dad5cf3de2dc0aba1c7c4b470754c69bd821f5faafc3d774bedc3b04bb7", - "sha256:0f6eff839a9e40e9c5610d3ff8c5bdd2f10303408312caf4c8003285d0b49565", - "sha256:1afcc2c33f31f6fb25c92d50a86b7a9f076d38acbcb6f9e74349636109550148", - "sha256:3896ae6c205d73ad192d2fc1489cd0edfab9f12867c85b4c277af4d37383c18c", - "sha256:47663efc9c395e31d09c6aacfa860f4473815ad6804311c5433f7085415d62a7", - "sha256:51be67e29f3cfcde263a113c28e96aa04362ed8229cb7c6e5f5c719003659d33", - "sha256:588f0d2da6cf1b1680974d63be09a6530fd1bd825dc87f76e162404779a157dc", - "sha256:6241afd72b628787b4abea39e238e3ff9f34165273fad306c7acf780dd850956", - "sha256:6647444b21cb5e68b593b970b2a9a07748dd74ea457c7dadaa15fd469c48ada1", - "sha256:68fcd2dc1b7d9310b29a15949cdd0cb9bc34b6de767aff979ebf546020bf0ba0", - "sha256:69b6f9a089d116a82c3ed819eea8fe67dae6105f0d81eaf0fdd5e60d0c6e0944", - "sha256:70fa38cdc66b2fc1349a082987f2b499d51d072faaa6b600f71931150de2e0e3", - "sha256:83333726e83ed44b0ac94d8d7a21bbdee4a05029c3b1e8db58a863eec8fd8a33", - "sha256:868a073fd0ff6468ae7d869b5fc1f54de5c4255b37f44fb890385eb68b68f95d", - "sha256:8b30a27f1cddf5c6efcb67e598d7823a1e253d743d92ac32ec1eb4b6a1417867", - "sha256:aac0ae0146a9bfa5e12d87dda89d9ef7c57a96210b899459fc2f785303dcbb67", - "sha256:ab1268db81aeb241200e321e220e7cd769762f386f92f61b898352dd27e402ce", - "sha256:b9ba6b6d34bd2563345488cf444510588ea42ad5613df3b3509f48eb80250afd", - "sha256:c51afd87c35c8331b56f796eff954b9c7f8d4b7fef5903daf4e05fcf017d23a8", - "sha256:cd57b13a6466822498238877892a9b287b0a58c2e81e4bdb0b596dbb151cbb73", - "sha256:d00d374a5625beeb448a7fa23060df79adb596074beb3ddc1838adb647b6ef09", - "sha256:d1b4e7176443d12610874bb84d0060bf080f000ea9ed7c84b2801df851320295", - "sha256:d7759994217c86c161c6a8060509cfdf782b952163569606bb373828afdd82e8", - "sha256:dc6fd330fd574c51d10638e63c0d00ab456498fc804c9d01f2a61b9264f2c5b2", - "sha256:e3ad79455c197a36eefbd90ad4aa832bece7f830a64396c15c61a0985e337287", - "sha256:e66442e084979a97bb66939e18f7b8709e4ac5f887e636aba29486ffbf373763", - "sha256:ee7490f0f3f16a6c38f8c680949551053c8194e68de5046e6c288e396dccee80", - "sha256:f8ce69f7bf01de2e2764e14df45b8404fc6f1a5ed9871e8e08a12169f87b7a26", - "sha256:fda7857e35993673fcda603c07d43889fca60a5b254052a462653f8656c64f44" - ], - "markers": "python_version >= '3.8'", - "version": "==13.0.0" - }, "pycparser": { "hashes": [ "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", @@ -2011,61 +1232,12 @@ "markers": "python_version >= '3.7'", "version": "==2.8.0" }, - "pyparsing": { - "hashes": [ - "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb", - "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db" - ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.1.1" - }, "pyperclip": { "hashes": [ "sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57" ], "version": "==1.8.2" }, - "pyproj": { - "hashes": [ - "sha256:052c49fce8b5d55943a35c36ccecb87350c68b48ba95bc02a789770c374ef819", - "sha256:1507138ea28bf2134d31797675380791cc1a7156a3aeda484e65a78a4aba9b62", - "sha256:1798ff7d65d9057ebb2d017ffe8403268b8452f24d0428b2140018c25c7fa1bc", - "sha256:1b7c2113c4d11184a238077ec85e31eda1dcc58ffeb9a4429830e0a7036e787d", - "sha256:2b708fd43453b985642b737d4a6e7f1d6a0ab1677ffa4e14cc258537b49224b0", - "sha256:385b0341861d3ebc8cad98337a738821dcb548d465576527399f4955ca24b6ed", - "sha256:38862fe07316ae12b79d82d298e390973a4f00b684f3c2d037238e20e00610ba", - "sha256:5674923351e76222e2c10c58b5e1ac119d7a46b270d822c463035971b06f724b", - "sha256:5c4b85ac10d733c42d73a2e6261c8d6745bf52433a31848dd1b6561c9a382da3", - "sha256:61e4ad57d89b03a7b173793b31bca8ee110112cde1937ef0f42a70b9120c827d", - "sha256:621d78a9d8bf4d06e08bef2471021fbcb1a65aa629ad4a20c22e521ce729cc20", - "sha256:6475ce653880938468a1a1b7321267243909e34b972ba9e53d5982c41d555918", - "sha256:6a87b419a2a352413fbf759ecb66da9da50bd19861c8f26db6a25439125b27b9", - "sha256:6f316a66031a14e9c5a88c91f8b77aa97f5454895674541ed6ab630b682be35d", - "sha256:71b65f2a38cd9e16883dbb0f8ae82bdf8f6b79b1b02975c78483ab8428dbbf2f", - "sha256:73f7960a97225812f9b1d7aeda5fb83812f38de9441e3476fcc8abb3e2b2f4de", - "sha256:7572983134e310e0ca809c63f1722557a040fe9443df5f247bf11ba887eb1229", - "sha256:788a5dadb532644a64efe0f5f01bf508c821eb7e984f13a677d56002f1e8a67a", - "sha256:7bdd2021bb6f7f346bfe1d2a358aa109da017d22c4704af2d994e7c7ee0a7a53", - "sha256:8fe6bb1b68a35d07378d38be77b5b2f8dd2bea5910c957bfcc7bee55988d3910", - "sha256:97ed199033c2c770e7eea2ef80ff5e6413426ec2d7ec985b869792f04ab95d05", - "sha256:9859d1591c1863414d875ae0759e72c2cffc01ab989dc64137fbac572cc81bf6", - "sha256:a730f5b4c98c8a0f312437873e6e34dbd4cc6dc23d5afd91a6691c62724b1f68", - "sha256:b60d93a200639e8367c6542a964fd0aa2dbd152f256c1831dc18cd5aa470fb8a", - "sha256:b752b7d9c4b08181c7e8c0d9c7f277cbefff42227f34d3310696a87c863d9dd3", - "sha256:b937215bfbaf404ec8f03ca741fc3f9f2c4c2c5590a02ccddddd820ae3c71331", - "sha256:c02742ef3d846401861a878a61ef7ad911ea7539d6cc4619ddb52dbdf7b45aee", - "sha256:cd5e2b6aa255023c4acd0b977590f1f7cc801ba21b4d806fcf6dfac3474ebb83", - "sha256:d711517a8487ef3245b08dc82f781a906df9abb3b6cb0ce0486f0eeb823ca570", - "sha256:d9a024370e917c899bff9171f03ea6079deecdc7482a146a2c565f3b9df134ea", - "sha256:e08db25b61cf024648d55973cc3d1c3f1d0818fabf594d5f5a8e2318103d2aa0", - "sha256:e97573de0ab3bbbcb4c7748bc41f4ceb6da10b45d35b1a294b5820701e7c25f0", - "sha256:eccb417b91d0be27805dfc97550bfb8b7db94e9fe1db5ebedb98f5b88d601323", - "sha256:f7c2f4d9681e810cf40239caaca00079930a6d9ee6591139b88d592d36051d82", - "sha256:fde5ece4d2436b5a57c8f5f97b49b5de06a856d03959f836c957d3e609f2de7e" - ], - "markers": "python_version >= '3.8'", - "version": "==3.5.0" - }, "pyrsistent": { "hashes": [ "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f", @@ -2115,12 +1287,12 @@ }, "pytest-asyncio": { "hashes": [ - "sha256:01da1bf94ff0b969cc8d760104f7c1011903d0d658bdaef0bbae769660a7fbc4", - "sha256:c09905acb3b79827aace4c67fe15097daea1b363c00c8236875395414914bad3" + "sha256:40a7eae6dded22c7b604986855ea48400ab15b069ae38116e8c01238e9eeb64d", + "sha256:8666c1c8ac02631d7c51ba282e0c69a8a452b211ffedf2599099845da5c5c37b" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==0.22.0" + "markers": "python_version >= '3.7'", + "version": "==0.21.1" }, "pytest-cov": { "hashes": [ @@ -2131,26 +1303,6 @@ "markers": "python_version >= '3.7'", "version": "==4.1.0" }, - "python-box": { - "hashes": [ - "sha256:0036fd47d388deaca8ebd65aea905f88ee6ef91d1d8ce34898b66f1824afbe80", - "sha256:1b3f346e332dba16df0b0543d319d9e7ce07d93e5ae152175302894352aa2d28", - "sha256:24c4ec0ee0278f66321100aaa9c615413da27a14ff43d376a2a3b4665e1d9494", - "sha256:2a3df244a5a79ac8f8447b5d11b5be0f2747d7b141cb2866060081ae9b53cc50", - "sha256:4ea13c98e05a3ec0ff26f254986a17290b69b5ade209fad081fd628f8fcfaa08", - "sha256:63b609555554d7a9d4b6e725f8e78ef1717c67e7d386200e03422ad612338df8", - "sha256:81ed1ec0f0ff2370227fc07277c5baca46d190a4747631bad7eb6ab1630fb7d9", - "sha256:8891735b4148e84d348c6eadd2f127152f751c9603e35d43a1f496183a291ac4", - "sha256:a0f1333c42e81529b6f68c192050df9d4505b803be7ac47f114036b98707f7cf", - "sha256:aabf8b9ae5dbc8ba431d8cbe0d4cfe737a25d52d68b0f5f2ff34915c21a2c1db", - "sha256:c046608337e723ae4de3206db5d1e1202ed166da2dfdc70c1f9361e72ace5633", - "sha256:d95e5eec4fc8f3fc5c9cc7347fc2eb4f9187c853d34c90b1658d1eff96cd4eac", - "sha256:f76b5b7f0cdc07bfdd4200dc24e6e33189bb2ae322137a2b7110fd41891a3157", - "sha256:f9266795e9c233874fb5b34fa994054b4fb0371881678e6ec45aec17fc95feac" - ], - "markers": "python_version >= '3.8'", - "version": "==7.1.1" - }, "python-dateutil": { "hashes": [ "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", @@ -2227,14 +1379,6 @@ "markers": "python_version >= '3.6'", "version": "==1.13" }, - "python-slugify": { - "hashes": [ - "sha256:70ca6ea68fe63ecc8fa4fcf00ae651fc8a5d02d93dcd12ae6d4fc7ca46c4d395", - "sha256:ce0d46ddb668b3be82f4ed5e503dbc33dd815d83e2eb6824211310d3fb172a27" - ], - "markers": "python_version >= '3.7'", - "version": "==8.0.1" - }, "pytz": { "hashes": [ "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b", @@ -2242,14 +1386,6 @@ ], "version": "==2023.3.post1" }, - "pytzdata": { - "hashes": [ - "sha256:3efa13b335a00a8de1d345ae41ec78dd11c9f8807f522d39850f2dd828681540", - "sha256:e1e14750bcf95016381e4d472bad004eef710f2d6417240904070b3d6654485f" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2020.1" - }, "pyyaml": { "hashes": [ "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", @@ -2483,14 +1619,6 @@ "markers": "python_version >= '3.8'", "version": "==68.2.2" }, - "sh": { - "hashes": [ - "sha256:9b2998f313f201c777e2c0061f0b1367497097ef13388595be147e2a00bf7ba1", - "sha256:ced8f2e081a858b66a46ace3703dec243779abbd5a1887ba7e3c34f34da70cd2" - ], - "markers": "python_version < '4.0' and python_full_version >= '3.8.1'", - "version": "==2.0.6" - }, "shapely": { "hashes": [ "sha256:03e63a99dfe6bd3beb8d5f41ec2086585bb969991d603f9aeac335ad396a06d4", @@ -2554,13 +1682,6 @@ "markers": "python_version >= '3.7'", "version": "==1.3.0" }, - "sortedcontainers": { - "hashes": [ - "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", - "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" - ], - "version": "==2.4.0" - }, "soupsieve": { "hashes": [ "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690", @@ -2615,14 +1736,6 @@ "markers": "python_version >= '3.8'", "version": "==23.2.0" }, - "tabulate": { - "hashes": [ - "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", - "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f" - ], - "markers": "python_version >= '3.7'", - "version": "==0.9.0" - }, "taskgroup": { "hashes": [ "sha256:5c1bd0e4c06114e7a4128583ab75c987597d5378a33948a3b74c662b90f61277", @@ -2631,22 +1744,6 @@ "markers": "python_version < '3.11'", "version": "==0.0.0a4" }, - "tblib": { - "hashes": [ - "sha256:80a6c77e59b55e83911e1e607c649836a69c103963c5f28a46cbeef44acf8129", - "sha256:93622790a0a29e04f0346458face1e144dc4d32f493714c6c3dff82a4adb77e6" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, - "tenacity": { - "hashes": [ - "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a", - "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c" - ], - "markers": "python_version >= '3.7'", - "version": "==8.2.3" - }, "testfixtures": { "hashes": [ "sha256:80774aecb0249458275ab783f53093fbe75795ff2b3218d22ce3fff0a12c4da6", @@ -2655,28 +1752,6 @@ "markers": "python_version >= '3.6'", "version": "==7.2.2" }, - "text-unidecode": { - "hashes": [ - "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", - "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93" - ], - "version": "==1.3" - }, - "textwrap3": { - "hashes": [ - "sha256:5008eeebdb236f6303dcd68f18b856d355f6197511d952ba74bc75e40e0c3414", - "sha256:bf5f4c40faf2a9ff00a9e0791fed5da7415481054cef45bb4a3cfb1f69044ae0" - ], - "version": "==0.9.2" - }, - "tinycss2": { - "hashes": [ - "sha256:2b80a96d41e7c3914b8cda8bc7f705a4d9c49275616e886103dd839dfc847847", - "sha256:8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627" - ], - "markers": "python_version >= '3.7'", - "version": "==1.2.1" - }, "tokenize-rt": { "hashes": [ "sha256:9fe80f8a5c1edad2d3ede0f37481cc0cc1538a2f442c9c2f9e4feacd2792d054", @@ -2684,14 +1759,6 @@ ], "version": "==5.2.0" }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.2" - }, "tomli": { "hashes": [ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", @@ -2700,31 +1767,6 @@ "markers": "python_version < '3.11'", "version": "==2.0.1" }, - "toolz": { - "hashes": [ - "sha256:2059bd4148deb1884bb0eb770a3cde70e7f954cfbbdc2285f1f2de01fd21eb6f", - "sha256:88c570861c440ee3f2f6037c4654613228ff40c93a6c25e0eba70d17282c6194" - ], - "markers": "python_version >= '3.5'", - "version": "==0.12.0" - }, - "tornado": { - "hashes": [ - "sha256:1bd19ca6c16882e4d37368e0152f99c099bad93e0950ce55e71daed74045908f", - "sha256:22d3c2fa10b5793da13c807e6fc38ff49a4f6e1e3868b0a6f4164768bb8e20f5", - "sha256:502fba735c84450974fec147340016ad928d29f1e91f49be168c0a4c18181e1d", - "sha256:65ceca9500383fbdf33a98c0087cb975b2ef3bfb874cb35b8de8740cf7f41bd3", - "sha256:71a8db65160a3c55d61839b7302a9a400074c9c753040455494e2af74e2501f2", - "sha256:7ac51f42808cca9b3613f51ffe2a965c8525cb1b00b7b2d56828b8045354f76a", - "sha256:7d01abc57ea0dbb51ddfed477dfe22719d376119844e33c661d873bf9c0e4a16", - "sha256:805d507b1f588320c26f7f097108eb4023bbaa984d63176d1652e184ba24270a", - "sha256:9dc4444c0defcd3929d5c1eb5706cbe1b116e762ff3e0deca8b715d14bf6ec17", - "sha256:ceb917a50cd35882b57600709dd5421a418c29ddc852da8bcdab1f0db33406b0", - "sha256:e7d8db41c0181c80d76c982aacc442c0783a2c54d6400fe028954201a2e032fe" - ], - "markers": "python_version >= '3.8'", - "version": "==6.3.3" - }, "tqdm": { "hashes": [ "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386", @@ -2839,21 +1881,6 @@ ], "version": "==0.2.9" }, - "webencodings": { - "hashes": [ - "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", - "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" - ], - "version": "==0.5.1" - }, - "websocket-client": { - "hashes": [ - "sha256:084072e0a7f5f347ef2ac3d8698a5e0b4ffbfcab607628cadabc650fc9a83a24", - "sha256:b3324019b3c28572086c4a319f91d1dcd44e6e11cd340232978c684a7650d0df" - ], - "markers": "python_version >= '3.8'", - "version": "==1.6.4" - }, "werkzeug": { "hashes": [ "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc", @@ -2878,14 +1905,6 @@ "markers": "python_full_version >= '3.7.0'", "version": "==1.2.0" }, - "zict": { - "hashes": [ - "sha256:5796e36bd0e0cc8cf0fbc1ace6a68912611c1dbd74750a3f3026b9b9d6a327ae", - "sha256:e321e263b6a97aafc0790c3cfb3c04656b7066e6738c37fffcca95d803c9fba5" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, "zipp": { "hashes": [ "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31", @@ -2914,99 +1933,99 @@ }, "charset-normalizer": { "hashes": [ - "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5", - "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93", - "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a", - "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d", - "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c", - "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1", - "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58", - "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2", - "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557", - "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147", - "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041", - "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2", - "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2", - "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7", - "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296", - "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690", - "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67", - "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57", - "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597", - "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846", - "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b", - "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97", - "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c", - "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62", - "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa", - "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f", - "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e", - "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821", - "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3", - "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4", - "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb", - "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727", - "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514", - "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d", - "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761", - "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55", - "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f", - "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c", - "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034", - "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6", - "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae", - "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1", - "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14", - "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1", - "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228", - "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708", - "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48", - "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f", - "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5", - "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f", - "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4", - "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8", - "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff", - "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61", - "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b", - "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97", - "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b", - "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605", - "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728", - "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d", - "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c", - "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf", - "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673", - "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1", - "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b", - "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41", - "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8", - "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f", - "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4", - "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008", - "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9", - "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5", - "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f", - "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e", - "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273", - "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45", - "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e", - "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656", - "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e", - "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c", - "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2", - "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72", - "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056", - "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397", - "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42", - "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd", - "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3", - "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213", - "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf", - "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67" + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.1" + "version": "==3.3.2" }, "docopt": { "hashes": [ @@ -3032,11 +2051,11 @@ }, "importlib-resources": { "hashes": [ - "sha256:9d48dcccc213325e810fd723e7fbb45ccb39f6cf5c31f00cf2b965f5f10f3cb9", - "sha256:aa50258bbfa56d4e33fbd8aa3ef48ded10d1735f11532b8df95388cc6bdb7e83" + "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a", + "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6" ], "markers": "python_version < '3.9'", - "version": "==6.1.0" + "version": "==6.1.1" }, "iniconfig": { "hashes": [ @@ -3086,11 +2105,11 @@ }, "pbr": { "hashes": [ - "sha256:567f09558bae2b3ab53cb3c1e2e33e726ff3338e7bae3db5dc954b3a44eef12b", - "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3" + "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda", + "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9" ], "markers": "python_version >= '2.6'", - "version": "==5.11.1" + "version": "==6.0.0" }, "pkgutil-resolve-name": { "hashes": [ @@ -3134,23 +2153,26 @@ }, "python-box": { "hashes": [ - "sha256:0036fd47d388deaca8ebd65aea905f88ee6ef91d1d8ce34898b66f1824afbe80", - "sha256:1b3f346e332dba16df0b0543d319d9e7ce07d93e5ae152175302894352aa2d28", - "sha256:24c4ec0ee0278f66321100aaa9c615413da27a14ff43d376a2a3b4665e1d9494", - "sha256:2a3df244a5a79ac8f8447b5d11b5be0f2747d7b141cb2866060081ae9b53cc50", - "sha256:4ea13c98e05a3ec0ff26f254986a17290b69b5ade209fad081fd628f8fcfaa08", - "sha256:63b609555554d7a9d4b6e725f8e78ef1717c67e7d386200e03422ad612338df8", - "sha256:81ed1ec0f0ff2370227fc07277c5baca46d190a4747631bad7eb6ab1630fb7d9", - "sha256:8891735b4148e84d348c6eadd2f127152f751c9603e35d43a1f496183a291ac4", - "sha256:a0f1333c42e81529b6f68c192050df9d4505b803be7ac47f114036b98707f7cf", - "sha256:aabf8b9ae5dbc8ba431d8cbe0d4cfe737a25d52d68b0f5f2ff34915c21a2c1db", - "sha256:c046608337e723ae4de3206db5d1e1202ed166da2dfdc70c1f9361e72ace5633", - "sha256:d95e5eec4fc8f3fc5c9cc7347fc2eb4f9187c853d34c90b1658d1eff96cd4eac", - "sha256:f76b5b7f0cdc07bfdd4200dc24e6e33189bb2ae322137a2b7110fd41891a3157", - "sha256:f9266795e9c233874fb5b34fa994054b4fb0371881678e6ec45aec17fc95feac" + "sha256:11cbe62f0dace8a6e2a10d210a5e87b99ad1a1286865568862516794c923a988", + "sha256:1d29eafaa287857751e27fbe9a08dd856480f0037fe988b221eba4dac33e5852", + "sha256:3638d3559f19ece7fa29f6a6550bc64696cd3b65e3d4154df07a3d06982252ff", + "sha256:3f0036f91e13958d2b37d2bc74c1197aa36ffd66755342eb64910f63d8a2990f", + "sha256:53998c3b95e31d1f31e46279ef1d27ac30b137746927260901ee61457f8468a0", + "sha256:594b0363b187df855ff8649488b1301dddbbeea769629b7caeb584efe779b841", + "sha256:6e7c243b356cb36e2c0f0e5ed7850969fede6aa812a7f501de7768996c7744d7", + "sha256:7b73f26e40a7adc57b9e39f5687d026dfa8a336f48aefaf852a223b4e37392ad", + "sha256:9dbd92b67c443a97326273c9239fce04d3b6958be815d293f96ab65bc4a9dae7", + "sha256:ab13208b053525ef154a36a4a52873b98a12b18b946edd4c939a4d5080e9a218", + "sha256:ac44b3b85714a4575cc273b5dbd39ef739f938ef6c522d6757704a29e7797d16", + "sha256:af6bcee7e1abe9251e9a41ca9ab677e1f679f6059321cfbae7e78a3831e0b736", + "sha256:bdec0a5f5a17b01fc538d292602a077aa8c641fb121e1900dff0591791af80e8", + "sha256:c14aa4e72bf30f4d573e62ff8030a86548603a100c3fb534561dbedf4a83f454", + "sha256:d199cd289b4f4d053770eadd70217c76214aac30b92a23adfb9627fd8558d300", + "sha256:ed6d7fe47d756dc2d9dea448702cea103716580a2efee7c859954929295fe28e", + "sha256:fa4696b5e09ccf695bf05c16bb5ca1fcc95a141a71a31eb262eee8e2ac07189a" ], - "markers": "python_version >= '3.8'", - "version": "==7.1.1" + "markers": "python_version >= '3.7'", + "version": "==6.1.0" }, "python-dateutil": { "hashes": [ @@ -3197,108 +2219,108 @@ }, "rpds-py": { "hashes": [ - "sha256:023574366002bf1bd751ebaf3e580aef4a468b3d3c216d2f3f7e16fdabd885ed", - "sha256:031f76fc87644a234883b51145e43985aa2d0c19b063e91d44379cd2786144f8", - "sha256:052a832078943d2b2627aea0d19381f607fe331cc0eb5df01991268253af8417", - "sha256:0699ab6b8c98df998c3eacf51a3b25864ca93dab157abe358af46dc95ecd9801", - "sha256:0713631d6e2d6c316c2f7b9320a34f44abb644fc487b77161d1724d883662e31", - "sha256:0774a46b38e70fdde0c6ded8d6d73115a7c39d7839a164cc833f170bbf539116", - "sha256:0898173249141ee99ffcd45e3829abe7bcee47d941af7434ccbf97717df020e5", - "sha256:09586f51a215d17efdb3a5f090d7cbf1633b7f3708f60a044757a5d48a83b393", - "sha256:102eac53bb0bf0f9a275b438e6cf6904904908562a1463a6fc3323cf47d7a532", - "sha256:10f32b53f424fc75ff7b713b2edb286fdbfc94bf16317890260a81c2c00385dc", - "sha256:150eec465dbc9cbca943c8e557a21afdcf9bab8aaabf386c44b794c2f94143d2", - "sha256:1d7360573f1e046cb3b0dceeb8864025aa78d98be4bb69f067ec1c40a9e2d9df", - "sha256:1f36a9d751f86455dc5278517e8b65580eeee37d61606183897f122c9e51cef3", - "sha256:24656dc36f866c33856baa3ab309da0b6a60f37d25d14be916bd3e79d9f3afcf", - "sha256:25860ed5c4e7f5e10c496ea78af46ae8d8468e0be745bd233bab9ca99bfd2647", - "sha256:26857f0f44f0e791f4a266595a7a09d21f6b589580ee0585f330aaccccb836e3", - "sha256:2bb2e4826be25e72013916eecd3d30f66fd076110de09f0e750163b416500721", - "sha256:2f6da6d842195fddc1cd34c3da8a40f6e99e4a113918faa5e60bf132f917c247", - "sha256:30adb75ecd7c2a52f5e76af50644b3e0b5ba036321c390b8e7ec1bb2a16dd43c", - "sha256:3339eca941568ed52d9ad0f1b8eb9fe0958fa245381747cecf2e9a78a5539c42", - "sha256:34ad87a831940521d462ac11f1774edf867c34172010f5390b2f06b85dcc6014", - "sha256:3777cc9dea0e6c464e4b24760664bd8831738cc582c1d8aacf1c3f546bef3f65", - "sha256:3953c6926a63f8ea5514644b7afb42659b505ece4183fdaaa8f61d978754349e", - "sha256:3c4eff26eddac49d52697a98ea01b0246e44ca82ab09354e94aae8823e8bda02", - "sha256:40578a6469e5d1df71b006936ce95804edb5df47b520c69cf5af264d462f2cbb", - "sha256:40f93086eef235623aa14dbddef1b9fb4b22b99454cb39a8d2e04c994fb9868c", - "sha256:4134aa2342f9b2ab6c33d5c172e40f9ef802c61bb9ca30d21782f6e035ed0043", - "sha256:442626328600bde1d09dc3bb00434f5374948838ce75c41a52152615689f9403", - "sha256:4a5ee600477b918ab345209eddafde9f91c0acd931f3776369585a1c55b04c57", - "sha256:4ce5a708d65a8dbf3748d2474b580d606b1b9f91b5c6ab2a316e0b0cf7a4ba50", - "sha256:516a611a2de12fbea70c78271e558f725c660ce38e0006f75139ba337d56b1f6", - "sha256:52c215eb46307c25f9fd2771cac8135d14b11a92ae48d17968eda5aa9aaf5071", - "sha256:53c43e10d398e365da2d4cc0bcaf0854b79b4c50ee9689652cdc72948e86f487", - "sha256:5752b761902cd15073a527b51de76bbae63d938dc7c5c4ad1e7d8df10e765138", - "sha256:5e8a78bd4879bff82daef48c14d5d4057f6856149094848c3ed0ecaf49f5aec2", - "sha256:5ed505ec6305abd2c2c9586a7b04fbd4baf42d4d684a9c12ec6110deefe2a063", - "sha256:5ee97c683eaface61d38ec9a489e353d36444cdebb128a27fe486a291647aff6", - "sha256:61fa268da6e2e1cd350739bb61011121fa550aa2545762e3dc02ea177ee4de35", - "sha256:64ccc28683666672d7c166ed465c09cee36e306c156e787acef3c0c62f90da5a", - "sha256:66414dafe4326bca200e165c2e789976cab2587ec71beb80f59f4796b786a238", - "sha256:68fe9199184c18d997d2e4293b34327c0009a78599ce703e15cd9a0f47349bba", - "sha256:6a555ae3d2e61118a9d3e549737bb4a56ff0cec88a22bd1dfcad5b4e04759175", - "sha256:6bdc11f9623870d75692cc33c59804b5a18d7b8a4b79ef0b00b773a27397d1f6", - "sha256:6cf4393c7b41abbf07c88eb83e8af5013606b1cdb7f6bc96b1b3536b53a574b8", - "sha256:6eef672de005736a6efd565577101277db6057f65640a813de6c2707dc69f396", - "sha256:734c41f9f57cc28658d98270d3436dba65bed0cfc730d115b290e970150c540d", - "sha256:73e0a78a9b843b8c2128028864901f55190401ba38aae685350cf69b98d9f7c9", - "sha256:775049dfa63fb58293990fc59473e659fcafd953bba1d00fc5f0631a8fd61977", - "sha256:7854a207ef77319ec457c1eb79c361b48807d252d94348305db4f4b62f40f7f3", - "sha256:78ca33811e1d95cac8c2e49cb86c0fb71f4d8409d8cbea0cb495b6dbddb30a55", - "sha256:79edd779cfc46b2e15b0830eecd8b4b93f1a96649bcb502453df471a54ce7977", - "sha256:7bf347b495b197992efc81a7408e9a83b931b2f056728529956a4d0858608b80", - "sha256:7fde6d0e00b2fd0dbbb40c0eeec463ef147819f23725eda58105ba9ca48744f4", - "sha256:81de24a1c51cfb32e1fbf018ab0bdbc79c04c035986526f76c33e3f9e0f3356c", - "sha256:879fb24304ead6b62dbe5034e7b644b71def53c70e19363f3c3be2705c17a3b4", - "sha256:8e7f2219cb72474571974d29a191714d822e58be1eb171f229732bc6fdedf0ac", - "sha256:9164ec8010327ab9af931d7ccd12ab8d8b5dc2f4c6a16cbdd9d087861eaaefa1", - "sha256:945eb4b6bb8144909b203a88a35e0a03d22b57aefb06c9b26c6e16d72e5eb0f0", - "sha256:99a57006b4ec39dbfb3ed67e5b27192792ffb0553206a107e4aadb39c5004cd5", - "sha256:9e9184fa6c52a74a5521e3e87badbf9692549c0fcced47443585876fcc47e469", - "sha256:9ff93d3aedef11f9c4540cf347f8bb135dd9323a2fc705633d83210d464c579d", - "sha256:a360cfd0881d36c6dc271992ce1eda65dba5e9368575663de993eeb4523d895f", - "sha256:a5d7ed104d158c0042a6a73799cf0eb576dfd5fc1ace9c47996e52320c37cb7c", - "sha256:ac17044876e64a8ea20ab132080ddc73b895b4abe9976e263b0e30ee5be7b9c2", - "sha256:ad857f42831e5b8d41a32437f88d86ead6c191455a3499c4b6d15e007936d4cf", - "sha256:b2039f8d545f20c4e52713eea51a275e62153ee96c8035a32b2abb772b6fc9e5", - "sha256:b455492cab07107bfe8711e20cd920cc96003e0da3c1f91297235b1603d2aca7", - "sha256:b4a9fe992887ac68256c930a2011255bae0bf5ec837475bc6f7edd7c8dfa254e", - "sha256:b5a53f5998b4bbff1cb2e967e66ab2addc67326a274567697379dd1e326bded7", - "sha256:b788276a3c114e9f51e257f2a6f544c32c02dab4aa7a5816b96444e3f9ffc336", - "sha256:bddd4f91eede9ca5275e70479ed3656e76c8cdaaa1b354e544cbcf94c6fc8ac4", - "sha256:c0503c5b681566e8b722fe8c4c47cce5c7a51f6935d5c7012c4aefe952a35eed", - "sha256:c1b3cd23d905589cb205710b3988fc8f46d4a198cf12862887b09d7aaa6bf9b9", - "sha256:c48f3fbc3e92c7dd6681a258d22f23adc2eb183c8cb1557d2fcc5a024e80b094", - "sha256:c63c3ef43f0b3fb00571cff6c3967cc261c0ebd14a0a134a12e83bdb8f49f21f", - "sha256:c6c45a2d2b68c51fe3d9352733fe048291e483376c94f7723458cfd7b473136b", - "sha256:caa1afc70a02645809c744eefb7d6ee8fef7e2fad170ffdeacca267fd2674f13", - "sha256:cc435d059f926fdc5b05822b1be4ff2a3a040f3ae0a7bbbe672babb468944722", - "sha256:cf693eb4a08eccc1a1b636e4392322582db2a47470d52e824b25eca7a3977b53", - "sha256:cf71343646756a072b85f228d35b1d7407da1669a3de3cf47f8bbafe0c8183a4", - "sha256:d08f63561c8a695afec4975fae445245386d645e3e446e6f260e81663bfd2e38", - "sha256:d29ddefeab1791e3c751e0189d5f4b3dbc0bbe033b06e9c333dca1f99e1d523e", - "sha256:d7f5e15c953ace2e8dde9824bdab4bec50adb91a5663df08d7d994240ae6fa31", - "sha256:d858532212f0650be12b6042ff4378dc2efbb7792a286bee4489eaa7ba010586", - "sha256:d97dd44683802000277bbf142fd9f6b271746b4846d0acaf0cefa6b2eaf2a7ad", - "sha256:dcdc88b6b01015da066da3fb76545e8bb9a6880a5ebf89e0f0b2e3ca557b3ab7", - "sha256:dd609fafdcdde6e67a139898196698af37438b035b25ad63704fd9097d9a3482", - "sha256:defa2c0c68734f4a82028c26bcc85e6b92cced99866af118cd6a89b734ad8e0d", - "sha256:e22260a4741a0e7a206e175232867b48a16e0401ef5bce3c67ca5b9705879066", - "sha256:e225a6a14ecf44499aadea165299092ab0cba918bb9ccd9304eab1138844490b", - "sha256:e3df0bc35e746cce42579826b89579d13fd27c3d5319a6afca9893a9b784ff1b", - "sha256:e6fcc026a3f27c1282c7ed24b7fcac82cdd70a0e84cc848c0841a3ab1e3dea2d", - "sha256:e782379c2028a3611285a795b89b99a52722946d19fc06f002f8b53e3ea26ea9", - "sha256:e8cdd52744f680346ff8c1ecdad5f4d11117e1724d4f4e1874f3a67598821069", - "sha256:e9616f5bd2595f7f4a04b67039d890348ab826e943a9bfdbe4938d0eba606971", - "sha256:e98c4c07ee4c4b3acf787e91b27688409d918212dfd34c872201273fdd5a0e18", - "sha256:ebdab79f42c5961682654b851f3f0fc68e6cc7cd8727c2ac4ffff955154123c1", - "sha256:f0f17f2ce0f3529177a5fff5525204fad7b43dd437d017dd0317f2746773443d", - "sha256:f4e56860a5af16a0fcfa070a0a20c42fbb2012eed1eb5ceeddcc7f8079214281" + "sha256:0525847f83f506aa1e28eb2057b696fe38217e12931c8b1b02198cfe6975e142", + "sha256:05942656cb2cb4989cd50ced52df16be94d344eae5097e8583966a1d27da73a5", + "sha256:0831d3ecdea22e4559cc1793f22e77067c9d8c451d55ae6a75bf1d116a8e7f42", + "sha256:0853da3d5e9bc6a07b2486054a410b7b03f34046c123c6561b535bb48cc509e1", + "sha256:08e6e7ff286254016b945e1ab632ee843e43d45e40683b66dd12b73791366dd1", + "sha256:0a38612d07a36138507d69646c470aedbfe2b75b43a4643f7bd8e51e52779624", + "sha256:0bedd91ae1dd142a4dc15970ed2c729ff6c73f33a40fa84ed0cdbf55de87c777", + "sha256:0c5441b7626c29dbd54a3f6f3713ec8e956b009f419ffdaaa3c80eaf98ddb523", + "sha256:0e9e976e0dbed4f51c56db10831c9623d0fd67aac02853fe5476262e5a22acb7", + "sha256:0fadfdda275c838cba5102c7f90a20f2abd7727bf8f4a2b654a5b617529c5c18", + "sha256:1096ca0bf2d3426cbe79d4ccc91dc5aaa73629b08ea2d8467375fad8447ce11a", + "sha256:171d9a159f1b2f42a42a64a985e4ba46fc7268c78299272ceba970743a67ee50", + "sha256:188912b22b6c8225f4c4ffa020a2baa6ad8fabb3c141a12dbe6edbb34e7f1425", + "sha256:1b4cf9ab9a0ae0cb122685209806d3f1dcb63b9fccdf1424fb42a129dc8c2faa", + "sha256:1e04581c6117ad9479b6cfae313e212fe0dfa226ac727755f0d539cd54792963", + "sha256:1fa73ed22c40a1bec98d7c93b5659cd35abcfa5a0a95ce876b91adbda170537c", + "sha256:2124f9e645a94ab7c853bc0a3644e0ca8ffbe5bb2d72db49aef8f9ec1c285733", + "sha256:240687b5be0f91fbde4936a329c9b7589d9259742766f74de575e1b2046575e4", + "sha256:25740fb56e8bd37692ed380e15ec734be44d7c71974d8993f452b4527814601e", + "sha256:27ccc93c7457ef890b0dd31564d2a05e1aca330623c942b7e818e9e7c2669ee4", + "sha256:281c8b219d4f4b3581b918b816764098d04964915b2f272d1476654143801aa2", + "sha256:2d34a5450a402b00d20aeb7632489ffa2556ca7b26f4a63c35f6fccae1977427", + "sha256:301bd744a1adaa2f6a5e06c98f1ac2b6f8dc31a5c23b838f862d65e32fca0d4b", + "sha256:30e5ce9f501fb1f970e4a59098028cf20676dee64fc496d55c33e04bbbee097d", + "sha256:33ab498f9ac30598b6406e2be1b45fd231195b83d948ebd4bd77f337cb6a2bff", + "sha256:35585a8cb5917161f42c2104567bb83a1d96194095fc54a543113ed5df9fa436", + "sha256:389c0e38358fdc4e38e9995e7291269a3aead7acfcf8942010ee7bc5baee091c", + "sha256:3acadbab8b59f63b87b518e09c4c64b142e7286b9ca7a208107d6f9f4c393c5c", + "sha256:3b7a64d43e2a1fa2dd46b678e00cabd9a49ebb123b339ce799204c44a593ae1c", + "sha256:3c8c0226c71bd0ce9892eaf6afa77ae8f43a3d9313124a03df0b389c01f832de", + "sha256:429349a510da82c85431f0f3e66212d83efe9fd2850f50f339341b6532c62fe4", + "sha256:466030a42724780794dea71eb32db83cc51214d66ab3fb3156edd88b9c8f0d78", + "sha256:47aeceb4363851d17f63069318ba5721ae695d9da55d599b4d6fb31508595278", + "sha256:48aa98987d54a46e13e6954880056c204700c65616af4395d1f0639eba11764b", + "sha256:4b2416ed743ec5debcf61e1242e012652a4348de14ecc7df3512da072b074440", + "sha256:4d0a675a7acbbc16179188d8c6d0afb8628604fc1241faf41007255957335a0b", + "sha256:4eb74d44776b0fb0782560ea84d986dffec8ddd94947f383eba2284b0f32e35e", + "sha256:4f8a1d990dc198a6c68ec3d9a637ba1ce489b38cbfb65440a27901afbc5df575", + "sha256:513ccbf7420c30e283c25c82d5a8f439d625a838d3ba69e79a110c260c46813f", + "sha256:5210a0018c7e09c75fa788648617ebba861ae242944111d3079034e14498223f", + "sha256:54cdfcda59251b9c2f87a05d038c2ae02121219a04d4a1e6fc345794295bdc07", + "sha256:56dd500411d03c5e9927a1eb55621e906837a83b02350a9dc401247d0353717c", + "sha256:57ec6baec231bb19bb5fd5fc7bae21231860a1605174b11585660236627e390e", + "sha256:5f1519b080d8ce0a814f17ad9fb49fb3a1d4d7ce5891f5c85fc38631ca3a8dc4", + "sha256:6174d6ad6b58a6bcf67afbbf1723420a53d06c4b89f4c50763d6fa0a6ac9afd2", + "sha256:68172622a5a57deb079a2c78511c40f91193548e8ab342c31e8cb0764d362459", + "sha256:6915fc9fa6b3ec3569566832e1bb03bd801c12cea030200e68663b9a87974e76", + "sha256:6b75b912a0baa033350367a8a07a8b2d44fd5b90c890bfbd063a8a5f945f644b", + "sha256:6f5dcb658d597410bb7c967c1d24eaf9377b0d621358cbe9d2ff804e5dd12e81", + "sha256:6f8d7fe73d1816eeb5378409adc658f9525ecbfaf9e1ede1e2d67a338b0c7348", + "sha256:7036316cc26b93e401cedd781a579be606dad174829e6ad9e9c5a0da6e036f80", + "sha256:7188ddc1a8887194f984fa4110d5a3d5b9b5cd35f6bafdff1b649049cbc0ce29", + "sha256:761531076df51309075133a6bc1db02d98ec7f66e22b064b1d513bc909f29743", + "sha256:7979d90ee2190d000129598c2b0c82f13053dba432b94e45e68253b09bb1f0f6", + "sha256:8015835494b21aa7abd3b43fdea0614ee35ef6b03db7ecba9beb58eadf01c24f", + "sha256:81c4d1a3a564775c44732b94135d06e33417e829ff25226c164664f4a1046213", + "sha256:81cf9d306c04df1b45971c13167dc3bad625808aa01281d55f3cf852dde0e206", + "sha256:88857060b690a57d2ea8569bca58758143c8faa4639fb17d745ce60ff84c867e", + "sha256:8c567c664fc2f44130a20edac73e0a867f8e012bf7370276f15c6adc3586c37c", + "sha256:91bd2b7cf0f4d252eec8b7046fa6a43cee17e8acdfc00eaa8b3dbf2f9a59d061", + "sha256:9620650c364c01ed5b497dcae7c3d4b948daeae6e1883ae185fef1c927b6b534", + "sha256:9b007c2444705a2dc4a525964fd4dd28c3320b19b3410da6517cab28716f27d3", + "sha256:9bf9acce44e967a5103fcd820fc7580c7b0ab8583eec4e2051aec560f7b31a63", + "sha256:a239303acb0315091d54c7ff36712dba24554993b9a93941cf301391d8a997ee", + "sha256:a2baa6be130e8a00b6cbb9f18a33611ec150b4537f8563bddadb54c1b74b8193", + "sha256:a54917b7e9cd3a67e429a630e237a90b096e0ba18897bfb99ee8bd1068a5fea0", + "sha256:a689e1ded7137552bea36305a7a16ad2b40be511740b80748d3140614993db98", + "sha256:a952ae3eb460c6712388ac2ec706d24b0e651b9396d90c9a9e0a69eb27737fdc", + "sha256:aa32205358a76bf578854bf31698a86dc8b2cb591fd1d79a833283f4a403f04b", + "sha256:b2287c09482949e0ca0c0eb68b2aca6cf57f8af8c6dfd29dcd3bc45f17b57978", + "sha256:b6b0e17d39d21698185097652c611f9cf30f7c56ccec189789920e3e7f1cee56", + "sha256:b710bf7e7ae61957d5c4026b486be593ed3ec3dca3e5be15e0f6d8cf5d0a4990", + "sha256:b8e11715178f3608874508f08e990d3771e0b8c66c73eb4e183038d600a9b274", + "sha256:b92aafcfab3d41580d54aca35a8057341f1cfc7c9af9e8bdfc652f83a20ced31", + "sha256:bec29b801b4adbf388314c0d050e851d53762ab424af22657021ce4b6eb41543", + "sha256:c694bee70ece3b232df4678448fdda245fd3b1bb4ba481fb6cd20e13bb784c46", + "sha256:c6b52b7028b547866c2413f614ee306c2d4eafdd444b1ff656bf3295bf1484aa", + "sha256:cb41ad20064e18a900dd427d7cf41cfaec83bcd1184001f3d91a1f76b3fcea4e", + "sha256:cd316dbcc74c76266ba94eb021b0cc090b97cca122f50bd7a845f587ff4bf03f", + "sha256:ced40cdbb6dd47a032725a038896cceae9ce267d340f59508b23537f05455431", + "sha256:d1c562a9bb72244fa767d1c1ab55ca1d92dd5f7c4d77878fee5483a22ffac808", + "sha256:d389ff1e95b6e46ebedccf7fd1fadd10559add595ac6a7c2ea730268325f832c", + "sha256:d56b1cd606ba4cedd64bb43479d56580e147c6ef3f5d1c5e64203a1adab784a2", + "sha256:d72a4315514e5a0b9837a086cb433b004eea630afb0cc129de76d77654a9606f", + "sha256:d9e7f29c00577aff6b318681e730a519b235af292732a149337f6aaa4d1c5e31", + "sha256:dbc25baa6abb205766fb8606f8263b02c3503a55957fcb4576a6bb0a59d37d10", + "sha256:e57919c32ee295a2fca458bb73e4b20b05c115627f96f95a10f9f5acbd61172d", + "sha256:e5bbe011a2cea9060fef1bb3d668a2fd8432b8888e6d92e74c9c794d3c101595", + "sha256:e6aea5c0eb5b0faf52c7b5c4a47c8bb64437173be97227c819ffa31801fa4e34", + "sha256:e888be685fa42d8b8a3d3911d5604d14db87538aa7d0b29b1a7ea80d354c732d", + "sha256:eebaf8c76c39604d52852366249ab807fe6f7a3ffb0dd5484b9944917244cdbe", + "sha256:efbe0b5e0fd078ed7b005faa0170da4f72666360f66f0bb2d7f73526ecfd99f9", + "sha256:efddca2d02254a52078c35cadad34762adbae3ff01c6b0c7787b59d038b63e0d", + "sha256:f05450fa1cd7c525c0b9d1a7916e595d3041ac0afbed2ff6926e5afb6a781b7f", + "sha256:f12d69d568f5647ec503b64932874dade5a20255736c89936bf690951a5e79f5", + "sha256:f45321224144c25a62052035ce96cbcf264667bcb0d81823b1bbc22c4addd194", + "sha256:f62581d7e884dd01ee1707b7c21148f61f2febb7de092ae2f108743fcbef5985", + "sha256:f8832a4f83d4782a8f5a7b831c47e8ffe164e43c2c148c8160ed9a6d630bc02a", + "sha256:fa35ad36440aaf1ac8332b4a4a433d4acd28f1613f0d480995f5cfd3580e90b7" ], "markers": "python_version >= '3.8'", - "version": "==0.10.6" + "version": "==0.12.0" }, "ruamel.yaml": { "hashes": [ From 8af061823c274d90d227b245a601e85ada1d6e2a Mon Sep 17 00:00:00 2001 From: James Harrison Date: Thu, 9 Nov 2023 09:43:07 +0000 Subject: [PATCH 5/7] Update codeQL to v2 --- .github/workflows/codeql-analysis.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 278af78d37..5ed01ce5fc 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -18,15 +18,10 @@ jobs: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. fetch-depth: 2 - - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 # Override language selection by uncommenting this and choosing your languages # with: # languages: go, javascript, csharp, python, cpp, java @@ -34,7 +29,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -48,4 +43,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 From be99d2039e10167b3bc9a145d6812d5e5df5cb56 Mon Sep 17 00:00:00 2001 From: James Harrison Date: Thu, 9 Nov 2023 09:45:59 +0000 Subject: [PATCH 6/7] Keep actions up-to-date with dependabot --- .github/dependabot.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b22ca32295..3ea0821b2a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -162,3 +162,8 @@ updates: - dependency-name: "pglast" # pglast version is pinned because different versions may produce different query IDs labels: - FlowMachine + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every week + interval: "weekly" From c26cd0fb85e2663811795c651d180a07fda8a616 Mon Sep 17 00:00:00 2001 From: James Harrison Date: Thu, 9 Nov 2023 12:21:49 +0000 Subject: [PATCH 7/7] Explicitly specify languages for CodeQL --- .github/workflows/codeql-analysis.yml | 67 +++++++++++++-------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5ed01ce5fc..ac9a0431a6 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -4,43 +4,42 @@ on: push: pull_request: schedule: - - cron: '0 13 * * 4' + - cron: "0 13 * * 4" jobs: CodeQL-Build: - runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - # Override language selection by uncommenting this and choosing your languages - # with: - # languages: go, javascript, csharp, python, cpp, java - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + # Override language selection by uncommenting this and choosing your languages + with: + languages: python, javascript #, go, csharp, cpp, java + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2